Файл: 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'] === 1 && $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, [1, 7, 14, 30], 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($out, chr(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(0, 23));
$weekday = Stats::weekdayHits($id, max(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',
[$id, time() - 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 ([1 => 'Сегодня', 7 => '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 > 0 ? $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'], 0, 80)) ?></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>