Вход Регистрация
Файл: new_top/modules/stats.php
Строк: 282
<?php
/**
 * Platform statistics — periods, in/CTR, weekday, hourly, rank delta, CSV
 */

declare(strict_types=1);

$id = (int)($_GET['id'] ?? 0);
$pf Database::fetch(
    
'SELECT p.*, c.name AS cat_name FROM platforms p LEFT JOIN cat c ON c.id = p.cat_id WHERE p.id = ?',
    [
$id]
);

$siteTitle Database::fetchColumn('SELECT title FROM config WHERE id = 1') ?: 'Top';

if (!
$pf || (int)$pf['ban'] === 1) {
    
http_response_code(404);
    
?><!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <?= pwa_meta() ?>
    <title>Не найдено — <?= e($siteTitle?></title>
<link rel="stylesheet" href="/assets/css/style.css">
</head>
<body>
<div class="app">
<header class="topbar">
    <div class="topbar-brand"><div class="logo">T</div><span><?= e($siteTitle?></span></div>
    <nav class="topbar-nav">
        <a href="/">Топ</a>
        <a href="/?module=office">Кабинет</a>
    </nav>
</header>
<div class="empty">
    <div class="empty-icon">🔍</div>
    <div>Площадка не найдена</div>
    <a href="/" class="btn btn-primary" style="margin-top:16px">На главную</a>
</div>
</div>
<?= pwa_register() ?>
</body>
</html>
    <?php
    
exit;
}

$uid = (int)($_SESSION['user_id'] ?? 0);
$lvl = (int)($_SESSION['user_level'] ?? 0);
if ((int)
$pf['hide'] === && $uid !== (int)$pf['user_id'] && $lvl 9) {
    
?><!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <?= pwa_meta() ?>
    <title>Статистика скрыта — <?= e($siteTitle?></title>
<link rel="stylesheet" href="/assets/css/style.css">
</head>
<body>
<div class="app">
<header class="topbar">
    <div class="topbar-brand"><div class="logo">T</div><span><?= e($siteTitle?></span></div>
    <nav class="topbar-nav">
        <a href="/">Топ</a>
        <a href="/?module=cat">Категории</a>
        <a href="/?module=office">Кабинет</a>
    </nav>
</header>
<h1 class="page-title"><?= e($pf['url']) ?></h1>
<p class="page-sub"><?= e($pf['cat_name'] ?? ''?></p>
<div class="empty">
    <div class="empty-icon">🔒</div>
    <div>Статистика скрыта владельцем</div>
    <p class="text-muted" style="margin-top:8px;font-size:.85rem">Владелец площадки отключил публичный просмотр статистики</p>
    <div class="actions" style="justify-content:center;margin-top:16px">
        <a href="/in/<?= (int)$pf['id'?>" class="btn btn-primary" target="_blank" rel="nofollow">Перейти на сайт</a>
        <a href="/" class="btn btn-ghost">На главную</a>
    </div>
</div>
<footer class="site-footer"><a href="/">← Вернуться в топ</a></footer>
</div>
</body>
</html>
    <?php
    
exit;
}

$period = (int)($_GET['period'] ?? 14);
if (!
in_array($period, [171430], true)) {
    
$period 14;
}

// CSV export — no HTML
if (isset($_GET['export']) && $_GET['export'] === 'csv') {
    
$hosts Stats::dailyHosts($id$period);
    
$hits Stats::dailyHits($id$period);
    
$ins Stats::dailyIn($id$period);
    
header('Content-Type: text/csv; charset=utf-8');
    
header('Content-Disposition: attachment; filename="stats_' $id '_' $period 'd.csv"');
    
$out fopen('php://output''w');
    
fprintf($outchr(0xEF) . chr(0xBB) . chr(0xBF)); // BOM for Excel
    
fputcsv($out, ['date''hosts''hits''in'], ';');
    foreach (
$hosts as $d => $h) {
        
fputcsv($out, [$d$h$hits[$d] ?? 0$ins[$d] ?? 0], ';');
    }
    
fclose($out);
    exit;
}

$hosts Stats::dailyHosts($id$period);
$hits  Stats::dailyHits($id$period);
$ins   Stats::dailyIn($id$period);
$labels Stats::shortLabels(array_keys($hosts));
$dev Stats::deviceSplit($id);
$online Stats::onlineCount($id);
$rankInfo Stats::rankDelta($id);
$rank = (int)($rankInfo['rank'] ?? 0);
$rankDelta $rankInfo['delta'];
$hourly Stats::hourlyHits($id);
$hourLabels array_map(static function ($h) { return sprintf('%02d'$h); }, range(023));
$weekday Stats::weekdayHits($idmax(28$period));
$totals Stats::periodTotals($id$period);
$onlineList Database::fetchAll(
    
'SELECT ip, ua, time FROM on_log WHERE pf = ? AND time > ? ORDER BY time DESC LIMIT 30',
    [
$idtime() - 600]
);
$title Database::fetchColumn('SELECT title FROM config WHERE id = 1') ?: 'Top';

$deltaStr '—';
$deltaClass '';
if (
$rankDelta !== null) {
    if (
$rankDelta 0) {
        
$deltaStr '↑' $rankDelta;
        
$deltaClass ' style="color:var(--accent)"';
    } elseif (
$rankDelta 0) {
        
$deltaStr '↓' abs($rankDelta);
        
$deltaClass ' style="color:var(--danger)"';
    } else {
        
$deltaStr '0';
    }
}

?><!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#0b0f1a">
<title>Статистика — <?= e($pf['url']) ?></title>
<link rel="stylesheet" href="/assets/css/style.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
</head>
<body>
<div class="app">
<header class="topbar">
    <div class="topbar-brand"><div class="logo">T</div><span><?= e($title?></span></div>
    <nav class="topbar-nav">
        <a href="/">Топ</a>
        <a href="/?module=cat">Категории</a>
        <a href="/?module=search">Поиск</a>
        <a href="/?module=office">Кабинет</a>
        <?php if (!empty($_SESSION['user_level']) && (int)$_SESSION['user_level'] >= 9): ?>
            <a href="/?module=admin">Админ</a>
            <?php endif; ?>
    </nav>
</header>

<h1 class="page-title"><?= e($pf['url']) ?></h1>
<p class="page-sub">
    <?= e($pf['cat_name'] ?? ''?>
    <?= (int)$pf['gold'] > time() ? ' · ★ GOLD' '' ?>
    · <a href="/in/<?= $id ?>">перейти</a>
</p>

<?= Ads::render('stats_top'?>
<?= Ads
::render('global_footer'?>

<nav class="tabs">
    <?php foreach ([=> 'Сегодня'=> '7 дней'14 => '14 дней'30 => '30 дней'] as $d => $lab): ?>
        <a class="<?= $period === $d 'active' '' ?>" href="/?module=stats&id=<?= $id ?>&period=<?= $d ?>"><?= $lab ?></a>
    <?php endforeach; ?>
    <a href="/?module=stats&id=<?= $id ?>&period=<?= $period ?>&export=csv">CSV</a>
</nav>

<div class="metric-row metric-row-4">
    <div class="metric">
        <div class="m-label">Место</div>
        <div class="m-val gold"><?= $rank $rank '—' ?></div>
        <div class="text-muted" style="font-size:.75rem;margin-top:2px"<?= $deltaClass ?>><?= e($deltaStr?> за сутки</div>
    </div>
    <div class="metric"><div class="m-label">Хосты (период)</div><div class="m-val"><?= number_format($totals['hosts'], 0''' '?></div></div>
    <div class="metric"><div class="m-label">Хиты (период)</div><div class="m-val blue"><?= number_format($totals['hits'], 0''' '?></div></div>
    <div class="metric"><div class="m-label">In / CTR</div><div class="m-val gold"><?= (int)$totals['in'?> <span style="font-size:.75rem;font-weight:600">/ <?= $totals['ctr'?>%</span></div></div>
    <div class="metric"><div class="m-label">Онлайн 10м</div><div class="m-val"><?= $online ?></div></div>
    <div class="metric"><div class="m-label">Хосты сегодня</div><div class="m-val"><?= number_format((int)$pf['hs'], 0''' '?></div></div>
    <div class="metric"><div class="m-label">Хиты сегодня</div><div class="m-val blue"><?= number_format((int)$pf['ht'], 0''' '?></div></div>
    <div class="metric"><div class="m-label">In всего</div><div class="m-val"><?= (int)$pf['in_count'?></div></div>
</div>

<div class="chart-card">
    <h3>Хосты · хиты · переходы in · <?= $period ?> дн.</h3>
    <div class="chart-wrap tall"><canvas id="mainChart"></canvas></div>
</div>

<div class="chart-grid chart-grid-2">
    <div class="chart-card">
        <h3>Сегодня по часам</h3>
        <div class="chart-wrap"><canvas id="hourChart"></canvas></div>
    </div>
    <div class="chart-card">
        <h3>По дням недели</h3>
        <div class="chart-wrap"><canvas id="weekChart"></canvas></div>
    </div>
</div>

<div class="chart-grid chart-grid-2">
    <div class="chart-card">
        <h3>Mobile / PC</h3>
        <div class="chart-wrap"><canvas id="devChart"></canvas></div>
    </div>
    <div class="chart-card">
        <h3>Всего за всё время</h3>
        <p class="text-muted" style="line-height:1.9;font-size:.95rem">
            Хосты: <strong style="color:var(--text)"><?= number_format((int)$pf['hs_all'], 0''' '?></strong><br>
            Хиты: <strong style="color:var(--text)"><?= number_format((int)$pf['ht_all'], 0''' '?></strong><br>
            Mobile: <?= (int)$pf['mb'?> · PC: <?= (int)$pf['pc'?><br>
            В топе с: <?= $pf['time_add'] ? date('d.m.Y', (int)$pf['time_add']) : '—' ?>
        </p>
    </div>
</div>

<div class="card">
    <h2>Онлайн (10 мин) · <?= (int)$online ?></h2>
    <?php if (!$onlineList): ?>
        <p class="text-muted">Сейчас никого нет</p>
    <?php else: ?>
        <?php foreach ($onlineList as $o): ?>
            <div style="padding:8px 0;border-bottom:1px solid var(--border);font-size:.82rem;color:var(--text-muted)">
                <span style="color:var(--accent)"><?= e(preg_replace('/.d+$/''.*'$o['ip'])) ?></span>
                · <?= date('H:i:s', (int)$o['time']) ?>
                <div style="opacity:.7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis"><?= e(mb_substr($o['ua'], 080)) ?></div>
            </div>
        <?php endforeach; ?>
    <?php endif; ?>
</div>

<footer class="site-footer"><a href="/">← Вернуться в топ</a></footer>
</div>
<script>
const grid='rgba(255,255,255,0.06)';
Chart.defaults.color='#8b9bb8';
Chart.defaults.borderColor=grid;
new Chart(document.getElementById('mainChart'), {
    type: 'line',
    data: {
        labels: <?= json_encode($labels?>,
        datasets: [
            { label: 'Хосты', data: <?= json_encode(array_values($hosts)) ?>, borderColor: '#00d4aa', backgroundColor: 'rgba(0,212,170,0.12)', fill: true, tension: 0.35, pointRadius: 2 },
            { label: 'Хиты', data: <?= json_encode(array_values($hits)) ?>, borderColor: '#5b8def', backgroundColor: 'rgba(91,141,239,0.1)', fill: true, tension: 0.35, pointRadius: 2 },
            { label: 'In', data: <?= json_encode(array_values($ins)) ?>, borderColor: '#f5c542', backgroundColor: 'rgba(245,197,66,0.1)', fill: false, tension: 0.35, pointRadius: 2 }
        ]
    },
    options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true } } }
});
new Chart(document.getElementById('hourChart'), {
    type: 'bar',
    data: {
        labels: <?= json_encode($hourLabels?>,
        datasets: [{ label: 'Хиты', data: <?= json_encode(array_values($hourly)) ?>, backgroundColor: 'rgba(91,141,239,0.7)', borderRadius: 4 }]
    },
    options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } }
});
new Chart(document.getElementById('weekChart'), {
    type: 'bar',
    data: {
        labels: <?= json_encode(array_keys($weekday)) ?>,
        datasets: [{ label: 'Хиты', data: <?= json_encode(array_values($weekday)) ?>, backgroundColor: 'rgba(0,212,170,0.7)', borderRadius: 6 }]
    },
    options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } }
});
new Chart(document.getElementById('devChart'), {
    type: 'doughnut',
    data: {
        labels: ['Mobile', 'PC'],
        datasets: [{ data: [<?= (int)$dev['mobile'?><?= (int)$dev['pc'?>], backgroundColor: ['#00d4aa', '#5b8def'], borderWidth: 0 }]
    },
    options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } }
});
</script>
</body>
</html>
Онлайн: 2
Реклама