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

803 lines
39 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', 'Analitik')
@section('page-title', 'Kullanıcı Analitikleri')
@push('styles')
<style>
.period-btn { padding: 6px 18px; border-radius: 99px; border: 1px solid var(--border2); background: transparent; color: var(--text2); font-size: 0.82rem; cursor: pointer; transition: all .15s; text-decoration: none; display: inline-block; font-weight:600; }
.period-btn.active, .period-btn:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.an-card { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px 22px; }
.an-card .val { font-size: 2rem; font-weight: 800; line-height: 1.1; color: var(--text); letter-spacing:-0.5px; }
.an-card .lbl { font-size: 0.78rem; color: var(--text2); margin-top: 4px; font-weight:600; }
.an-card .ico { font-size: 1.8rem; opacity: 0.5; }
.an-card .delta { font-size: 0.72rem; font-weight: 600; margin-top: 6px; }
.delta-up { color: var(--success); }
.delta-down { color: var(--danger); }
.chart-card { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px 20px; }
.chart-card .chart-title { font-size: 0.88rem; font-weight: 700; color: var(--text); margin-bottom: 14px; }
.chart-wrap { position: relative; }
.geo-flag { font-size: 1.1rem; }
.pbar-wrap { height: 6px; background: var(--surface); border-radius: 3px; flex: 1; }
.pbar-fill { height: 100%; background: linear-gradient(90deg,var(--accent),var(--accent-lt)); border-radius: 3px; transition: width .4s; }
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--success); display: inline-block; animation: blink 1.2s infinite; }
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:.3} }
.tbl-sm th { font-size: 0.73rem; color: var(--text2); text-transform: uppercase; letter-spacing: .5px; padding: 8px 10px; }
.tbl-sm td { font-size: 0.84rem; padding: 8px 10px; vertical-align: middle; border-top: 1px solid var(--border); color: var(--text); }
.tbl-sm tr:first-child td { border-top: none; }
.badge-type { font-size: 0.7rem; padding: 2px 8px; border-radius: 20px; }
.badge-home { background: rgba(6,182,212,0.12); color: var(--info); }
.badge-anime { background: rgba(16,185,129,0.12); color: var(--success); }
.badge-player { background: rgba(124,58,237,0.12); color: var(--accent-lt); }
.badge-search { background: rgba(245,158,11,0.12); color: var(--warning); }
.badge-ai { background: rgba(124,58,237,0.12); color: var(--accent-lt); }
.badge-genre { background: rgba(6,182,212,0.12); color: var(--info); }
.badge-other { background: rgba(255,255,255,0.06); color: var(--text2); }
.pct-pill { font-size: 0.7rem; padding: 1px 7px; border-radius: 10px; }
.pct-high { background: var(--success); color: #fff; }
.pct-mid { background: var(--warning); color: #000; }
.pct-low { background: var(--surface); color: var(--text2); }
.ai-q-text { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.82rem; color: var(--text2); }
.section-head { font-size: 0.78rem; text-transform: uppercase; letter-spacing: .7px; color: var(--text2); font-weight: 700; margin-bottom: 14px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
</style>
@endpush
@section('content')
<div class="content-wrap">
{{-- Period seçici --}}
<div class="d-flex align-items-center gap-2 mb-4 flex-wrap">
<span class="text-muted me-1" style="font-size:.82rem">Dönem:</span>
@foreach(['today'=>'Bugün','7d'=>'7 Gün','30d'=>'30 Gün','90d'=>'90 Gün'] as $key=>$lbl)
<a href="{{ route('admin.analytics.index', ['period'=>$key]) }}"
class="period-btn {{ $period===$key ? 'active' : '' }}">{{ $lbl }}</a>
@endforeach
<span class="ms-auto text-muted" style="font-size:.75rem">
<span class="live-dot me-1"></span> Canlı Son güncelleme: {{ now()->format('H:i') }}
</span>
</div>
{{-- Özet kartlar --}}
<div class="row g-3 mb-4">
<div class="col-6 col-sm-4 col-lg">
<div class="an-card h-100">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="val">{{ number_format($totalViews) }}</div>
<div class="lbl">Sayfa Görüntüleme</div>
@if($period === '7d' || $period === 'today')
<div class="delta {{ $viewsDelta >= 0 ? 'delta-up' : 'delta-down' }}">
<i class="bi bi-arrow-{{ $viewsDelta >= 0 ? 'up' : 'down' }}"></i>
{{ abs($viewsDelta) }}% dünden
</div>
@endif
</div>
<i class="bi bi-eye ico text-info"></i>
</div>
</div>
</div>
<div class="col-6 col-sm-4 col-lg">
<div class="an-card h-100">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="val">{{ number_format($uniqueVisitors) }}</div>
<div class="lbl">Tekil Ziyaretçi</div>
</div>
<i class="bi bi-people ico text-success"></i>
</div>
</div>
</div>
<div class="col-6 col-sm-4 col-lg">
<div class="an-card h-100">
<div class="d-flex justify-content-between align-items-start">
<div>
@php
$wh = floor($watchSeconds / 3600);
$wm = floor(($watchSeconds % 3600) / 60);
@endphp
<div class="val">{{ $wh > 0 ? $wh.'s' : $wm.'d' }}</div>
<div class="lbl">Toplam İzleme Süresi</div>
<div class="delta" style="color:var(--text2)">{{ number_format($watchSeconds/3600, 1) }} saat</div>
</div>
<i class="bi bi-play-circle ico" style="color:#a371f7"></i>
</div>
</div>
</div>
<div class="col-6 col-sm-4 col-lg">
<div class="an-card h-100">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="val">{{ number_format($aiTotal) }}</div>
<div class="lbl">AI Sorgusu</div>
</div>
<i class="bi bi-stars ico" style="color:#e8393c"></i>
</div>
</div>
</div>
<div class="col-6 col-sm-4 col-lg">
<div class="an-card h-100">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="val">{{ number_format($newUsers) }}</div>
<div class="lbl">Yeni Kayıt</div>
</div>
<i class="bi bi-person-plus ico" style="color:#f0883e"></i>
</div>
</div>
</div>
</div>
{{-- Görüntüleme trendi + Cihaz dağılımı --}}
<div class="row g-3 mb-4">
<div class="col-lg-8">
<div class="chart-card h-100">
<div class="d-flex align-items-center justify-content-between mb-3">
<div class="chart-title mb-0">Görüntüleme ve İzleme Trendi</div>
<div class="d-flex gap-3" style="font-size:.72rem;color:var(--text2)">
<span><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:var(--accent);margin-right:4px"></span>Görüntüleme</span>
<span><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#a371f7;margin-right:4px"></span>İzleme (saat)</span>
</div>
</div>
<div class="chart-wrap" style="height:220px">
<canvas id="trendChart"></canvas>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="chart-card h-100">
<div class="chart-title">Cihaz Dağılımı</div>
<div class="chart-wrap" style="height:160px;display:flex;align-items:center;justify-content:center">
<canvas id="deviceChart"></canvas>
</div>
<div class="mt-3">
@php $deviceTotal = $deviceStats->sum(); @endphp
@foreach(['mobile'=>['bi-phone','var(--accent)'],'desktop'=>['bi-laptop','var(--success)'],'tablet'=>['bi-tablet','#79c0ff']] as $dv=>[$ico,$col])
@if(($cnt=$deviceStats[$dv]??0) > 0)
<div class="d-flex align-items-center gap-2 mb-2">
<i class="bi {{ $ico }}" style="color:{{ $col }};width:16px"></i>
<span style="font-size:.82rem;width:60px">{{ ucfirst($dv) }}</span>
<div class="pbar-wrap"><div class="pbar-fill" style="width:{{ $deviceTotal>0?round($cnt/$deviceTotal*100):0 }}%;background:{{ $col }}"></div></div>
<span style="font-size:.78rem;color:var(--text2);width:40px;text-align:right">{{ number_format($cnt) }}</span>
</div>
@endif
@endforeach
</div>
</div>
</div>
</div>
{{-- Top anime + AI sorgular --}}
<div class="row g-3 mb-4">
<div class="col-lg-7">
<div class="chart-card h-100">
<div class="chart-title">En Çok İzlenen 10 Anime</div>
@if($topAnimes->count())
@php $maxV = $topAnimes->max('views') ?: 1; @endphp
@foreach($topAnimes as $i => $a)
<div class="d-flex align-items-center gap-2 mb-2">
<span style="font-size:.72rem;color:var(--text2);width:16px;text-align:right">{{ $i+1 }}</span>
<span style="font-size:.84rem;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ $a['title'] }}</span>
<div class="pbar-wrap" style="width:120px"><div class="pbar-fill" style="width:{{ round($a['views']/$maxV*100) }}%"></div></div>
<span style="font-size:.78rem;color:var(--text2);width:42px;text-align:right">{{ number_format($a['views']) }}</span>
</div>
@endforeach
@else
<div class="text-muted text-center py-4" style="font-size:.85rem">Bu dönemde veri yok</div>
@endif
</div>
</div>
<div class="col-lg-5">
<div class="chart-card h-100">
<div class="chart-title">AI Sorgu Türleri</div>
<div class="chart-wrap" style="height:160px;display:flex;align-items:center;justify-content:center">
<canvas id="aiChart"></canvas>
</div>
@php
$aiLabels = ['chat'=>'Sohbet','recommend'=>'Öneri','search'=>'Arama','episode_info'=>'Bölüm Analiz','similar'=>'Benzer'];
$aiColors = ['chat'=>'var(--accent)','recommend'=>'#a371f7','search'=>'var(--success)','episode_info'=>'#f0883e','similar'=>'#79c0ff'];
$aiTotalQ = $aiByType->sum();
@endphp
<div class="mt-3">
@foreach($aiByType->sortDesc() as $type => $cnt)
<div class="d-flex align-items-center gap-2 mb-2">
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:{{ $aiColors[$type]??'var(--text2)' }}"></span>
<span style="font-size:.82rem;flex:1">{{ $aiLabels[$type]??$type }}</span>
<span style="font-size:.78rem;color:var(--text2)">{{ number_format($cnt) }}</span>
<span style="font-size:.72rem;color:#6e7681">{{ $aiTotalQ>0?round($cnt/$aiTotalQ*100):0 }}%</span>
</div>
@endforeach
@if($aiByType->isEmpty())
<div class="text-muted text-center py-2" style="font-size:.82rem">Bu dönemde AI sorgusu yok</div>
@endif
</div>
</div>
</div>
</div>
{{-- Saatlik dağılım --}}
<div class="row g-3 mb-4">
<div class="col-12">
<div class="chart-card">
<div class="chart-title">Bugünkü Saatlik Görüntüleme Dağılımı</div>
<div class="chart-wrap" style="height:130px">
<canvas id="hourlyChart"></canvas>
</div>
</div>
</div>
</div>
{{-- Top bölümler + Coğrafi --}}
<div class="row g-3 mb-4">
<div class="col-lg-7">
<div class="chart-card h-100">
<div class="section-head">En Çok İzlenen Bölümler (İzleme Süresi)</div>
@if($topEpisodes->count())
<table class="table mb-0 tbl-sm" style="color:#c9d1d9">
<thead><tr>
<th>Anime</th><th>Bölüm</th><th>Oynatma</th><th>Süre</th><th>Ort. %</th>
</tr></thead>
<tbody>
@foreach($topEpisodes as $ep)
<tr>
<td style="max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ $ep['anime'] }}</td>
<td><span class="badge" style="background:var(--border);color:#c9d1d9">{{ $ep['label'] }}</span></td>
<td>{{ number_format($ep['plays']) }}</td>
<td>{{ $ep['hours'] }} s</td>
<td>
@php $pct=$ep['avg_pct']??0; @endphp
<span class="pct-pill {{ $pct>=70?'pct-high':($pct>=40?'pct-mid':'pct-low') }}">%{{ $pct }}</span>
</td>
</tr>
@endforeach
</tbody>
</table>
@else
<div class="text-muted text-center py-4" style="font-size:.85rem">Bu dönemde izleme verisi yok</div>
@endif
</div>
</div>
<div class="col-lg-5">
<div class="chart-card h-100">
<div class="section-head">Coğrafi Dağılım (Şehir)</div>
@if($geoStats->count())
@php $maxGeo = $geoStats->max('cnt') ?: 1; @endphp
@foreach($geoStats as $g)
<div class="d-flex align-items-center gap-2 mb-2">
<span style="font-size:.82rem;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
{{ $g->city }}
<span style="color:var(--text2);font-size:.75rem"> · {{ $g->country }}</span>
</span>
<div class="pbar-wrap" style="width:80px"><div class="pbar-fill" style="width:{{ round($g->cnt/$maxGeo*100) }}%"></div></div>
<span style="font-size:.75rem;color:var(--text2);width:32px;text-align:right">{{ $g->cnt }}</span>
</div>
@endforeach
@else
<div class="text-muted text-center py-4" style="font-size:.85rem">Coğrafi veri yok (IP geolocation bekliyor)</div>
@endif
</div>
</div>
</div>
{{-- En aktif kullanıcılar + AI top sorular --}}
<div class="row g-3 mb-4">
<div class="col-lg-6">
<div class="chart-card h-100">
<div class="section-head">En Aktif Kullanıcılar</div>
@if($activeUsers->count())
<table class="table mb-0 tbl-sm" style="color:#c9d1d9">
<thead><tr>
<th>#</th><th>Kullanıcı</th><th>Görüntüleme</th><th>Aktif Gün</th>
</tr></thead>
<tbody>
@foreach($activeUsers as $i => $au)
<tr>
<td style="color:var(--text2)">{{ $i+1 }}</td>
<td>
<div style="font-size:.83rem">{{ $au['user']->name }}</div>
<div style="font-size:.72rem;color:var(--text2)">{{ $au['user']->email }}</div>
</td>
<td>{{ number_format($au['views']) }}</td>
<td>{{ $au['days'] }} gün</td>
</tr>
@endforeach
</tbody>
</table>
@else
<div class="text-muted text-center py-4" style="font-size:.85rem">Bu dönemde giriş yapan kullanıcı yok</div>
@endif
</div>
</div>
<div class="col-lg-6">
<div class="chart-card h-100">
<div class="section-head">Popüler AI Soruları (Sohbet)</div>
@if($aiTopQuestions->count())
@foreach($aiTopQuestions as $q)
<div class="d-flex align-items-start gap-2 mb-3">
<i class="bi bi-chat-right-text" style="color:var(--accent);margin-top:2px;flex-shrink:0"></i>
<div class="flex-1" style="flex:1">
<div class="ai-q-text" title="{{ $q->query_text }}">{{ $q->query_text }}</div>
<div style="font-size:.7rem;color:var(--text2);margin-top:2px">{{ $q->cnt }} kez soruldu</div>
</div>
</div>
@endforeach
@else
<div class="text-muted text-center py-4" style="font-size:.85rem">Bu dönemde chat sorusu yok</div>
@endif
@if($aiTopUsers->count())
<div class="section-head mt-3">AI'ı En Çok Kullananlar</div>
@foreach($aiTopUsers as $au)
<div class="d-flex align-items-center justify-content-between mb-2" style="font-size:.83rem">
<span>{{ $au['name'] }}</span>
<span class="badge" style="background:var(--border);color:#a371f7">{{ $au['cnt'] }} sorgu</span>
</div>
@endforeach
@endif
</div>
</div>
</div>
{{-- Sayfa türleri + Tarayıcılar --}}
<div class="row g-3 mb-4">
<div class="col-lg-6">
<div class="chart-card">
<div class="section-head">Sayfa Türü Dağılımı</div>
@php
$ptLabels = ['home'=>'Ana Sayfa','anime'=>'Anime Detay','player'=>'Video Player','search'=>'Arama','ai'=>'AI Hub','genre'=>'Tür','other'=>'Diğer'];
$ptColors = ['home'=>'#79c0ff','anime'=>'var(--success)','player'=>'#a371f7','search'=>'#f0883e','ai'=>'var(--accent)','genre'=>'#ffa657','other'=>'#6e7681'];
$ptTotal = $pageTypeStats->sum();
@endphp
<div class="row">
@foreach($pageTypeStats->sortDesc() as $pt => $cnt)
<div class="col-6 mb-3">
<div class="d-flex align-items-center gap-2">
<span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:{{ $ptColors[$pt]??'var(--text2)' }};flex-shrink:0"></span>
<span style="font-size:.82rem;flex:1">{{ $ptLabels[$pt]??$pt }}</span>
<span style="font-size:.78rem;color:var(--text2)">{{ number_format($cnt) }}</span>
</div>
<div class="pbar-wrap mt-1"><div class="pbar-fill" style="width:{{ $ptTotal>0?round($cnt/$ptTotal*100):0 }}%;background:{{ $ptColors[$pt]??'var(--text2)' }}"></div></div>
</div>
@endforeach
</div>
</div>
</div>
<div class="col-lg-6">
<div class="chart-card">
<div class="section-head">Tarayıcı Dağılımı</div>
@php $brTotal = $browserStats->sum(); @endphp
@foreach($browserStats->sortDesc()->take(6) as $br => $cnt)
<div class="d-flex align-items-center gap-2 mb-2">
<span style="font-size:.82rem;width:80px">{{ $br ?: 'Diğer' }}</span>
<div class="pbar-wrap"><div class="pbar-fill" style="width:{{ $brTotal>0?round($cnt/$brTotal*100):0 }}%;background:var(--accent)"></div></div>
<span style="font-size:.78rem;color:var(--text2);width:40px;text-align:right">{{ number_format($cnt) }}</span>
<span style="font-size:.72rem;color:#6e7681;width:32px;text-align:right">{{ $brTotal>0?round($cnt/$brTotal*100):0 }}%</span>
</div>
@endforeach
</div>
</div>
</div>
{{-- Trafik Kaynakları --}}
<div class="row g-3 mb-4">
<div class="col-lg-7">
<div class="chart-card h-100">
<div class="section-head">Trafik Kaynakları (Referrer)</div>
@php
$refTotal = $referrerStats->sum() + $directTraffic;
$allSources = collect(['Direkt / Boş' => $directTraffic])->merge($referrerStats)->sortDesc();
$srcColors = ['Direkt / Boş'=>'var(--text2)'];
$palette = ['var(--accent)','var(--success)','#79c0ff','#f0883e','#a371f7','#ffa657','#58a6ff','#7ee787','#ff7bc6','#d2a8ff','#ffa198','#56d364'];
$pi = 0;
foreach($referrerStats->keys() as $k) { $srcColors[$k] = $palette[$pi % count($palette)]; $pi++; }
@endphp
@if($allSources->sum() > 0)
@foreach($allSources->take(12) as $src => $cnt)
<div class="d-flex align-items-center gap-2 mb-2">
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:{{ $srcColors[$src]??'var(--text2)' }};flex-shrink:0"></span>
<span style="font-size:.82rem;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="{{ $src }}">
@if($src === 'Direkt / Boş')
<i class="bi bi-link-45deg me-1" style="color:var(--text2)"></i>{{ $src }}
@else
<i class="bi bi-globe2 me-1" style="color:{{ $srcColors[$src]??'var(--text2)' }}"></i>{{ $src }}
@endif
</span>
<div class="pbar-wrap" style="width:100px"><div class="pbar-fill" style="width:{{ $refTotal>0?round($cnt/$refTotal*100):0 }}%;background:{{ $srcColors[$src]??'var(--text2)' }}"></div></div>
<span style="font-size:.78rem;color:var(--text2);width:38px;text-align:right">{{ number_format($cnt) }}</span>
<span style="font-size:.72rem;color:#6e7681;width:30px;text-align:right">{{ $refTotal>0?round($cnt/$refTotal*100):0 }}%</span>
</div>
@endforeach
@else
<div class="text-muted text-center py-4" style="font-size:.85rem">Bu dönemde referrer verisi yok</div>
@endif
</div>
</div>
<div class="col-lg-5">
<div class="chart-card h-100">
<div class="section-head">Kaynak Dağılımı</div>
<div class="chart-wrap" style="height:180px;display:flex;align-items:center;justify-content:center">
<canvas id="referrerChart"></canvas>
</div>
<div class="mt-3 text-center">
<div style="font-size:.78rem;color:var(--text2)">
Toplam kayıtlı trafik: <strong style="color:var(--text)">{{ number_format($refTotal) }}</strong>
</div>
<div style="font-size:.75rem;color:#6e7681;margin-top:4px">
Direkt: {{ $refTotal>0?round($directTraffic/$refTotal*100):0 }}% · Referral: {{ $refTotal>0?round($referrerStats->sum()/$refTotal*100):0 }}%
</div>
</div>
</div>
</div>
</div>
{{-- Son aktiviteler (canlı akış) --}}
<div class="chart-card mb-4">
<div class="d-flex align-items-center justify-content-between mb-3">
<div class="section-head mb-0">Son Sayfa Görüntülemeleri</div>
<span class="live-dot"></span>
</div>
<table class="table mb-0 tbl-sm" style="color:#c9d1d9">
<thead><tr>
<th>Zaman</th><th>Kullanıcı</th><th>Sayfa Türü</th><th>URL</th><th>Şehir</th><th>Cihaz</th>
</tr></thead>
<tbody>
@forelse($recentViews as $rv)
<tr>
<td style="white-space:nowrap;color:var(--text2);font-size:.75rem">{{ $rv->created_at->diffForHumans() }}</td>
<td>
@if($rv->user)
<span style="font-size:.82rem">{{ $rv->user->name }}</span>
@else
<span style="color:#6e7681;font-size:.78rem">Misafir</span>
@endif
</td>
<td><span class="badge-type badge-{{ $rv->page_type }}">{{ $rv->page_type }}</span></td>
<td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.75rem;color:var(--text2)" title="{{ $rv->url }}">{{ $rv->url }}</td>
<td style="font-size:.78rem">{{ $rv->city ?: '—' }}</td>
<td><i class="bi bi-{{ match($rv->device){'mobile'=>'phone','tablet'=>'tablet',default=>'laptop'} }}" style="color:var(--text2)"></i></td>
</tr>
@empty
<tr><td colspan="6" class="text-center text-muted py-4" style="font-size:.85rem">Bu dönemde görüntüleme yok</td></tr>
@endforelse
</tbody>
</table>
</div>
{{-- ── BOT TRAFİK PANELİ ─────────────────────────────────────────────────── --}}
<div class="an-card mt-4">
<div class="section-head" style="display:flex;align-items:center;justify-content:space-between">
<span>🤖 Bot & Güvenlik</span>
<span style="font-size:.78rem;color:var(--text2)">Son dönem</span>
</div>
{{-- Bot özet kartları --}}
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-bottom:20px">
<div class="an-card" style="text-align:center">
<div style="font-size:1.6rem;font-weight:800;color:var(--danger)">{{ number_format($botViews) }}</div>
<div style="font-size:.78rem;color:var(--text2)">Bot İsteği</div>
</div>
<div class="an-card" style="text-align:center">
<div style="font-size:1.6rem;font-weight:800;color:var(--success)">{{ number_format($humanViews) }}</div>
<div style="font-size:.78rem;color:var(--text2)">İnsan İsteği</div>
</div>
<div class="an-card" style="text-align:center">
<div style="font-size:1.6rem;font-weight:800;color:{{ ($botRatio ?? 0) > 50 ? 'var(--danger)' : '#d29922' }}">{{ $botRatio ?? 0 }}%</div>
<div style="font-size:.78rem;color:var(--text2)">Bot Oranı</div>
</div>
<div class="an-card" style="text-align:center">
<div style="font-size:1.6rem;font-weight:800;color:var(--info)">{{ $blockedIps->count() }}</div>
<div style="font-size:.78rem;color:var(--text2)">Engelli IP</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px">
{{-- En çok vuran botlar --}}
<div>
<div style="font-size:.82rem;font-weight:700;color:var(--text);margin-bottom:10px">Top Bot IP'leri</div>
<table class="table table-sm" style="font-size:.78rem">
<thead><tr><th>IP</th><th>İstek</th><th>Durum</th><th></th></tr></thead>
<tbody>
@forelse($botTopIps as $b)
<tr>
<td><code style="color:var(--info);font-size:.75rem">{{ $b->ip }}</code></td>
<td>{{ number_format($b->cnt) }}</td>
<td>
<span style="font-size:.7rem;padding:2px 7px;border-radius:4px;background:{{ $b->action === 'blocked' ? 'rgba(248,81,73,.15)' : 'rgba(63,185,80,.15)' }};color:{{ $b->action === 'blocked' ? 'var(--danger)' : 'var(--success)' }}">
{{ $b->action }}
</span>
</td>
<td>
<form method="POST" action="{{ route('admin.analytics.block-ip') }}" style="display:inline">
@csrf
<input type="hidden" name="ip" value="{{ $b->ip }}">
<input type="hidden" name="reason" value="Admin: bot trafiği">
<button type="submit" class="btn btn-sm" style="padding:1px 8px;font-size:.7rem;background:rgba(248,81,73,.15);border:1px solid rgba(248,81,73,.3);color:var(--danger);border-radius:4px">Engelle</button>
</form>
</td>
</tr>
@empty
<tr><td colspan="4" class="text-muted text-center py-3">Bot logu yok</td></tr>
@endforelse
</tbody>
</table>
</div>
{{-- Bot türleri --}}
<div>
<div style="font-size:.82rem;font-weight:700;color:var(--text);margin-bottom:10px">Bot Türleri</div>
<table class="table table-sm" style="font-size:.78rem">
<thead><tr><th>Bot</th><th>İstek</th><th>Durum</th></tr></thead>
<tbody>
@forelse($botByName as $b)
<tr>
<td style="max-width:150px;overflow:hidden;text-overflow:ellipsis">{{ $b->bot_name ?: '' }}</td>
<td>{{ number_format($b->cnt) }}</td>
<td>
<span style="font-size:.7rem;padding:2px 7px;border-radius:4px;background:{{ $b->action === 'blocked' ? 'rgba(248,81,73,.15)' : 'rgba(210,153,34,.15)' }};color:{{ $b->action === 'blocked' ? 'var(--danger)' : '#d29922' }}">
{{ $b->action }}
</span>
</td>
</tr>
@empty
<tr><td colspan="3" class="text-muted text-center py-3">Kayıt yok</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
{{-- Manuel IP Engelleme --}}
<div style="margin-top:20px;border-top:1px solid var(--border);padding-top:16px">
<div style="font-size:.82rem;font-weight:700;color:var(--text);margin-bottom:10px">Manuel IP Engelle</div>
<form method="POST" action="{{ route('admin.analytics.block-ip') }}" style="display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end">
@csrf
<div>
<label style="font-size:.75rem;color:var(--text2);display:block;margin-bottom:4px">IP Adresi</label>
<input type="text" name="ip" class="form-control form-control-sm" placeholder="1.2.3.4" style="width:160px">
</div>
<div>
<label style="font-size:.75rem;color:var(--text2);display:block;margin-bottom:4px">Sebep</label>
<input type="text" name="reason" class="form-control form-control-sm" placeholder="Manuel engel" style="width:200px">
</div>
<div>
<label style="font-size:.75rem;color:var(--text2);display:block;margin-bottom:4px">Bitiş (boşsa kalıcı)</label>
<input type="date" name="expires_at" class="form-control form-control-sm" style="width:150px">
</div>
<button type="submit" class="btn btn-sm" style="background:rgba(248,81,73,.2);border:1px solid rgba(248,81,73,.4);color:var(--danger);border-radius:6px">Engelle</button>
</form>
</div>
{{-- Engelli IP listesi --}}
@if($blockedIps->count())
<div style="margin-top:16px">
<div style="font-size:.82rem;font-weight:700;color:var(--text);margin-bottom:8px">Aktif Engeller</div>
<table class="table table-sm" style="font-size:.78rem">
<thead><tr><th>IP</th><th>Sebep</th><th>Tür</th><th>Bitiş</th><th></th></tr></thead>
<tbody>
@foreach($blockedIps as $bi)
<tr>
<td><code style="color:var(--danger);font-size:.75rem">{{ $bi->ip }}</code></td>
<td>{{ $bi->reason ?: '—' }}</td>
<td><span style="font-size:.7rem;color:{{ $bi->auto_blocked ? '#d29922' : 'var(--text2)' }}">{{ $bi->auto_blocked ? 'Otomatik' : 'Manuel' }}</span></td>
<td style="font-size:.75rem;color:var(--text2)">{{ $bi->expires_at ? \Carbon\Carbon::parse($bi->expires_at)->diffForHumans() : 'Kalıcı' }}</td>
<td>
<form method="POST" action="{{ route('admin.analytics.unblock-ip') }}" style="display:inline">
@csrf
<input type="hidden" name="ip" value="{{ $bi->ip }}">
<button type="submit" class="btn btn-sm" style="padding:1px 8px;font-size:.7rem;background:rgba(63,185,80,.1);border:1px solid rgba(63,185,80,.3);color:var(--success);border-radius:4px">Kaldır</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
{{-- ── OTURUM DETAYLARI ────────────────────────────────────────────────────── --}}
<div class="an-card mt-4">
<div class="section-head" style="display:flex;align-items:center;justify-content:space-between">
<span>📊 Oturum Analizi</span>
<div style="display:flex;gap:16px;font-size:.82rem">
<span>Ort. Süre: <strong style="color:var(--info)">{{ gmdate('i:s', $avgSessionTime) }}</strong></span>
<span>Ort. Sayfa: <strong style="color:var(--info)">{{ $avgPages }}</strong></span>
</div>
</div>
<table class="table table-sm" style="font-size:.78rem">
<thead>
<tr>
<th>IP</th><th>Ülke</th><th>Cihaz</th><th>Tarayıcı</th>
<th>Sayfalar</th><th>Süre</th><th>Kaynak</th><th>Bot?</th><th>Başlangıç</th>
</tr>
</thead>
<tbody>
@forelse($sessions as $s)
<tr style="{{ $s->is_bot ? 'opacity:.5' : '' }}">
<td><code style="font-size:.72rem;color:var(--info)">{{ $s->ip }}</code></td>
<td>{{ $s->country ?: '—' }}</td>
<td><i class="bi bi-{{ match($s->device ?? ''){ 'mobile'=>'phone','tablet'=>'tablet',default=>'laptop'} }}"></i></td>
<td>{{ $s->browser ?: '—' }}</td>
<td>{{ $s->pages_visited }}</td>
<td>{{ gmdate('i:s', $s->total_seconds) }}</td>
<td style="max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="{{ $s->referrer }}">{{ $s->referrer ? parse_url($s->referrer, PHP_URL_HOST) : 'Direkt' }}</td>
<td>
@if($s->is_bot)
<span style="font-size:.7rem;color:var(--danger)">{{ $s->bot_type }}</span>
@else
<span style="font-size:.7rem;color:var(--success)">İnsan</span>
@endif
</td>
<td style="color:var(--text2)">{{ \Carbon\Carbon::parse($s->started_at)->diffForHumans() }}</td>
</tr>
@empty
<tr><td colspan="9" class="text-center text-muted py-4">Oturum kaydı yok</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.2/dist/chart.umd.min.js"></script>
<script>
Chart.defaults.color = 'var(--text2)';
Chart.defaults.borderColor = 'var(--border)';
// ── Trend grafiği ────────────────────────────────────────────────────────────
new Chart(document.getElementById('trendChart'), {
type: 'line',
data: {
labels: @json($trendLabels),
datasets: [
{
label: 'Görüntüleme',
data: @json($trendData),
borderColor: 'var(--accent)',
backgroundColor: 'rgba(232,67,147,.08)',
fill: true,
tension: 0.4,
pointRadius: 3,
pointBackgroundColor: 'var(--accent)',
yAxisID: 'y',
},
{
label: 'İzleme (saat)',
data: @json($watchTrendData),
borderColor: '#a371f7',
backgroundColor: 'rgba(163,113,247,.08)',
fill: true,
tension: 0.4,
pointRadius: 3,
pointBackgroundColor: '#a371f7',
yAxisID: 'y1',
}
]
},
options: {
responsive: true, maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
plugins: { legend: { display: false } },
scales: {
x: { grid: { color: 'var(--border)' }, ticks: { maxTicksLimit: 12, font: { size: 10 } } },
y: { grid: { color: 'var(--border)' }, ticks: { font: { size: 10 } }, beginAtZero: true },
y1: { position: 'right', grid: { display: false }, ticks: { font: { size: 10 } }, beginAtZero: true },
}
}
});
// ── Cihaz pasta grafiği ──────────────────────────────────────────────────────
@php
$dvKeys = ['mobile','desktop','tablet','unknown'];
$dvData = array_map(fn($k) => $deviceStats[$k] ?? 0, $dvKeys);
@endphp
new Chart(document.getElementById('deviceChart'), {
type: 'doughnut',
data: {
labels: ['Mobil', 'Masaüstü', 'Tablet', 'Diğer'],
datasets: [{
data: @json($dvData),
backgroundColor: ['var(--accent)','var(--success)','#79c0ff','#6e7681'],
borderWidth: 0,
hoverOffset: 4,
}]
},
options: {
responsive: true, maintainAspectRatio: false,
plugins: { legend: { display: false }, tooltip: { callbacks: {
label: ctx => ` ${ctx.label}: ${ctx.parsed.toLocaleString()}`
}}}
}
});
// ── AI sorgu türleri grafiği ─────────────────────────────────────────────────
@php
$aiK = $aiByType->keys()->toArray();
$aiV = $aiByType->values()->toArray();
$aiColorMap = ['chat'=>'var(--accent)','recommend'=>'#a371f7','search'=>'var(--success)','episode_info'=>'#f0883e','similar'=>'#79c0ff'];
$aiC = array_map(fn($k) => $aiColorMap[$k] ?? 'var(--text2)', $aiK);
$aiLabelMap = ['chat'=>'Sohbet','recommend'=>'Öneri','search'=>'Arama','episode_info'=>'Bölüm','similar'=>'Benzer'];
$aiL = array_map(fn($k) => $aiLabelMap[$k] ?? $k, $aiK);
@endphp
new Chart(document.getElementById('aiChart'), {
type: 'doughnut',
data: {
labels: @json($aiL),
datasets: [{
data: @json($aiV),
backgroundColor: @json($aiC),
borderWidth: 0,
hoverOffset: 4,
}]
},
options: {
responsive: true, maintainAspectRatio: false,
plugins: { legend: { display: false } }
}
});
// ── Saatlik dağılım ──────────────────────────────────────────────────────────
new Chart(document.getElementById('hourlyChart'), {
type: 'bar',
data: {
labels: Array.from({length:24}, (_,i) => `${String(i).padStart(2,'0')}:00`),
datasets: [{
label: 'Görüntüleme',
data: @json($hourlyData),
backgroundColor: 'rgba(232,67,147,.5)',
borderColor: 'var(--accent)',
borderWidth: 1,
borderRadius: 3,
}]
},
options: {
responsive: true, maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
x: { grid: { display: false }, ticks: { font: { size: 9 } } },
y: { grid: { color: 'var(--border)' }, ticks: { font: { size: 10 } }, beginAtZero: true },
}
}
});
// ── Referrer pasta grafiği ────────────────────────────────────────────────────
@php
$refChartLabels = $allSources->take(8)->keys()->map(fn($k) => strlen($k)>22 ? substr($k,0,20).'…' : $k)->values()->toArray();
$refChartData = $allSources->take(8)->values()->toArray();
$refChartColors = $allSources->take(8)->keys()->map(fn($k) => $srcColors[$k] ?? 'var(--text2)')->values()->toArray();
@endphp
new Chart(document.getElementById('referrerChart'), {
type: 'doughnut',
data: {
labels: @json($refChartLabels),
datasets: [{
data: @json($refChartData),
backgroundColor: @json($refChartColors),
borderWidth: 0,
hoverOffset: 4,
}]
},
options: {
responsive: true, maintainAspectRatio: false,
plugins: { legend: { display: false }, tooltip: { callbacks: {
label: ctx => ` ${ctx.label}: ${ctx.parsed.toLocaleString()}`
}}}
}
});
// ── Otomatik yenileme (60 saniye) ────────────────────────────────────────────
setTimeout(() => location.reload(), 60000);
</script>
@endpush