%PDF- <> %âãÏÓ endobj 2 0 obj <> endobj 3 0 obj <>/ExtGState<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/Annots[ 28 0 R 29 0 R] /MediaBox[ 0 0 595.5 842.25] /Contents 4 0 R/Group<>/Tabs/S>> endobj ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµùÕ5sLOšuY>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<> endobj 2 0 obj<>endobj 2 0 obj<>es 3 0 R>> endobj 2 0 obj<> ox[ 0.000000 0.000000 609.600000 935.600000]/Fi endobj 3 0 obj<> endobj 7 1 obj<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Subtype/Form>> stream
<?php
require_once dirname(__DIR__) . '/config/config.php';
require_once dirname(__DIR__) . '/includes/auth.php';
require_once dirname(__DIR__) . '/includes/queries.php';
require_central();
$totalVisitors = (int) db()->query('SELECT COUNT(*) FROM analytics_visitor')->fetchColumn();
$totalViews = (int) db()->query('SELECT COUNT(*) FROM analytics_pageview')->fetchColumn();
$todayViews = (int) db()->query('SELECT COUNT(*) FROM analytics_pageview WHERE created_at >= CURDATE()')->fetchColumn();
$avgDur = (int) round((float) db()->query('SELECT COALESCE(AVG(duration),0) FROM analytics_pageview WHERE duration IS NOT NULL')->fetchColumn());
$topPages = db()->query(
"SELECT path, MAX(title) AS title, COUNT(*) AS views, COUNT(DISTINCT vid) AS uniq, ROUND(AVG(duration)) AS avgdur
FROM analytics_pageview
GROUP BY path
ORDER BY views DESC
LIMIT 25"
)->fetchAll();
$maxViews = 0;
foreach ($topPages as $p) {
$maxViews = max($maxViews, (int) $p['views']);
}
// Tren 14 hari terakhir.
$trendRows = db()->query(
"SELECT DATE(created_at) AS d, COUNT(*) AS c
FROM analytics_pageview
WHERE created_at >= (CURDATE() - INTERVAL 13 DAY)
GROUP BY DATE(created_at)"
)->fetchAll(PDO::FETCH_KEY_PAIR);
$trend = [];
for ($i = 13; $i >= 0; $i--) {
$day = date('Y-m-d', strtotime("-$i day"));
$trend[] = ['label' => date('d/m', strtotime($day)), 'value' => (int) ($trendRows[$day] ?? 0)];
}
$pageLabel = static function (array $p): array {
$path = $p['path'] === '/' ? '/' : rtrim($p['path'], '/');
$title = trim((string) ($p['title'] ?? ''));
if ($title === '') {
$title = $path === '/' ? 'Beranda' : $path;
}
return ['title' => $title, 'path' => $path];
};
$pageTitle = 'Analitik Pengunjung — CMS PPID';
$activeMenu = 'analitik';
require __DIR__ . '/partials/head.php';
?>
<div class="mb-6">
<h2 class="text-2xl font-bold text-slate-900">Analitik Pengunjung</h2>
<p class="text-sm text-slate-500 mt-1">Statistik kunjungan situs publik (anonim, tanpa data pribadi).</p>
</div>
<!-- Kartu ringkasan -->
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6 mb-6">
<?php
$cards = [
['fa-user-group', 'green', 'Pengunjung Unik', number_format($totalVisitors)],
['fa-eye', 'blue', 'Total Kunjungan Halaman', number_format($totalViews)],
['fa-calendar-day', 'gold', 'Kunjungan Hari Ini', number_format($todayViews)],
['fa-clock', 'slate', 'Rata-rata Durasi', format_durasi($avgDur)],
];
foreach ($cards as [$icon, $warna, $label, $val]):
$c = ['green' => 'bg-green-50 text-mateng-green', 'blue' => 'bg-blue-50 text-mateng-blue', 'gold' => 'bg-amber-50 text-mateng-gold', 'slate' => 'bg-slate-100 text-slate-500'][$warna]; ?>
<div class="bg-white rounded-xl p-5 border border-slate-100 shadow-sm">
<div class="w-11 h-11 rounded-lg <?= $c ?> flex items-center justify-center text-lg mb-3"><i class="fas <?= $icon ?>"></i></div>
<p class="text-2xl font-bold text-slate-900 leading-tight"><?= e($val) ?></p>
<p class="text-xs text-slate-500 font-medium mt-1"><?= e($label) ?></p>
</div>
<?php endforeach; ?>
</div>
<?php if ($totalViews === 0): ?>
<div class="text-center py-20 bg-white rounded-xl border border-slate-100">
<i class="fas fa-chart-line text-4xl text-slate-300 mb-3"></i>
<p class="text-slate-500 font-medium">Belum ada data kunjungan. Data akan muncul saat pengunjung membuka situs publik.</p>
</div>
<?php else: ?>
<!-- Tren 14 hari -->
<div class="bg-white rounded-xl p-5 sm:p-6 border border-slate-100 shadow-sm mb-6">
<h3 class="font-bold text-slate-800 mb-4 flex items-center gap-2"><i class="fas fa-chart-line text-mateng-green"></i> Kunjungan 14 Hari Terakhir</h3>
<canvas id="trendChart" height="90"></canvas>
</div>
<!-- Halaman terpopuler -->
<div class="bg-white border border-slate-100 rounded-xl shadow-sm overflow-hidden">
<div class="px-5 py-4 border-b border-slate-100">
<h3 class="font-bold text-slate-800 flex items-center gap-2"><i class="fas fa-fire text-mateng-gold"></i> Halaman Paling Sering Dikunjungi</h3>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead>
<tr class="bg-slate-50 text-slate-500 text-xs uppercase tracking-wider">
<th class="px-5 py-3.5 font-semibold">Halaman</th>
<th class="px-5 py-3.5 font-semibold text-center">Kunjungan</th>
<th class="px-5 py-3.5 font-semibold text-center">Pengunjung Unik</th>
<th class="px-5 py-3.5 font-semibold text-center">Rata-rata Durasi</th>
</tr>
</thead>
<tbody class="text-sm divide-y divide-slate-100 text-slate-700">
<?php foreach ($topPages as $p): $lbl = $pageLabel($p); $bar = $maxViews > 0 ? round((int) $p['views'] / $maxViews * 100) : 0; ?>
<tr class="hover:bg-slate-50/50">
<td class="px-5 py-3.5">
<div class="font-medium text-slate-800 truncate max-w-xs"><?= e($lbl['title']) ?></div>
<div class="text-xs text-slate-400 truncate max-w-xs"><?= e($lbl['path']) ?></div>
</td>
<td class="px-5 py-3.5">
<div class="flex items-center gap-2">
<div class="flex-grow h-2 bg-slate-100 rounded-full overflow-hidden min-w-[60px]"><div class="h-full bg-mateng-green rounded-full" style="width:<?= $bar ?>%"></div></div>
<span class="font-semibold text-slate-700 w-10 text-right"><?= number_format((int) $p['views']) ?></span>
</div>
</td>
<td class="px-5 py-3.5 text-center"><?= number_format((int) $p['uniq']) ?></td>
<td class="px-5 py-3.5 text-center text-slate-500"><?= $p['avgdur'] !== null ? e(format_durasi((int) $p['avgdur'])) : '—' ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"></script>
<script>
(function () {
var el = document.getElementById('trendChart');
if (!el || !window.Chart) { return; }
new Chart(el, {
type: 'line',
data: {
labels: <?= json_encode(array_column($trend, 'label')) ?>,
datasets: [{
data: <?= json_encode(array_column($trend, 'value')) ?>,
borderColor: '#15803d',
backgroundColor: 'rgba(21,128,61,.08)',
fill: true, tension: .35, pointRadius: 3, pointBackgroundColor: '#15803d'
}]
},
options: {
responsive: true, maintainAspectRatio: true,
plugins: { legend: { display: false } },
scales: { y: { beginAtZero: true, ticks: { precision: 0 } } }
}
});
})();
</script>
<?php endif; ?>
<?php require __DIR__ . '/partials/foot.php'; ?>