Diff -- /var/www/vhosts/3dshawn.com/abforge.3dshawn.com/TEMPLATES/abforge_admin_traffic.html
Diff
/var/www/vhosts/3dshawn.com/abforge.3dshawn.com/TEMPLATES/abforge_admin_traffic.html
added on local at 2026-07-01 16:01:46
Added
+189
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 89c0a9609f40
to 89c0a9609f40
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.| 1 | <!-- ===================================================================== | |
| 2 | ADMIN TRAFFIC REPORTS — /admin_traffic.cgi | |
| 3 | Cross-tenant version of traffic_reports. Same tab shape, no | |
| 4 | site_id filter (platform-wide data). | |
| 5 | ===================================================================== --> | |
| 6 | ||
| 7 | <style> | |
| 8 | .at-tabs { display:flex;gap:0;border-bottom:1px solid var(--col-border);margin-bottom:24px;flex-wrap:wrap; } | |
| 9 | .at-tab { padding:10px 18px;font-size:13px;font-weight:600;color:var(--col-text-2);text-decoration:none;border-bottom:2px solid transparent; } | |
| 10 | .at-tab:hover { color:var(--col-text);background:var(--col-surface-2); } | |
| 11 | .at-tab.is-active { color:var(--col-accent-bright);border-bottom-color:var(--col-accent); } | |
| 12 | ||
| 13 | .at-kpi-grid { display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:24px; } | |
| 14 | .at-kpi { background:var(--col-surface-1);border:1px solid var(--col-border);border-radius:12px;padding:16px 18px; } | |
| 15 | .at-kpi-label { font-size:11px;font-weight:700;color:var(--col-text-3);letter-spacing:1.2px;text-transform:uppercase;margin-bottom:6px; } | |
| 16 | .at-kpi-value { font-family:var(--font-display,sans-serif);font-size:28px;color:var(--col-text);line-height:1; } | |
| 17 | ||
| 18 | .at-card { background:var(--col-surface-1);border:1px solid var(--col-border);border-radius:14px;padding:20px;margin-bottom:16px; } | |
| 19 | .at-card-title { font-family:var(--font-display,sans-serif);font-size:18px;color:var(--col-text);margin:0 0 4px; } | |
| 20 | .at-card-sub { font-size:12px;color:var(--col-text-3);margin-bottom:14px; } | |
| 21 | ||
| 22 | .at-two-col { display:grid;grid-template-columns:1fr 1fr;gap:16px; } | |
| 23 | .at-three-col { display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px; } | |
| 24 | @media (max-width:900px) { .at-two-col { grid-template-columns:1fr; } } | |
| 25 | ||
| 26 | table.at-table { width:100%;border-collapse:collapse; } | |
| 27 | table.at-table th { text-align:left;padding:9px 12px;font-size:11px;font-weight:700;color:var(--col-text-3);letter-spacing:1px;text-transform:uppercase;border-bottom:1px solid var(--col-border); } | |
| 28 | table.at-table td { padding:10px 12px;font-size:13px;color:var(--col-text-2);border-bottom:1px solid var(--col-border); } | |
| 29 | table.at-table tr:last-child td { border-bottom:none; } | |
| 30 | table.at-table tr:hover td { background:var(--col-surface-2);color:var(--col-text); } | |
| 31 | .at-bar { display:inline-block;width:60px;height:6px;border-radius:3px;background:var(--col-surface-2);overflow:hidden;vertical-align:middle;margin-left:6px; } | |
| 32 | .at-bar-fill { display:block;height:100%;background:linear-gradient(90deg,var(--col-accent),var(--col-accent-bright)); } | |
| 33 | ||
| 34 | .at-visitor-row { display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--col-border); } | |
| 35 | .at-visitor-row:last-child { border-bottom:none; } | |
| 36 | .at-dot { width:8px;height:8px;border-radius:50%;background:var(--col-text-3);flex-shrink:0; } | |
| 37 | .at-dot.active { background:#4ade80;box-shadow:0 0 8px #4ade80;animation:atPulse 1.4s infinite ease-in-out; } | |
| 38 | @keyframes atPulse { 0%,100%{opacity:1} 50%{opacity:0.35} } | |
| 39 | .at-visitor-meta { font-size:12px;color:var(--col-text-3); } | |
| 40 | ||
| 41 | .at-empty { background:var(--col-surface-1);border:1px solid var(--col-border);border-radius:14px;padding:40px;text-align:center;color:var(--col-text-3); } | |
| 42 | </style> | |
| 43 | ||
| 44 | ||
| 45 | <div style="margin-bottom:18px"> | |
| 46 | <span class="page-eyebrow"><span class="dot"></span> Admin · Platform traffic</span> | |
| 47 | <h1 class="page-title">Platform-wide traffic</h1> | |
| 48 | <p class="page-subtitle">Visitors, audience, and heatmaps across every site on the platform.</p> | |
| 49 | </div> | |
| 50 | ||
| 51 | [if:$schema_missing] | |
| 52 | <div class="at-empty"> | |
| 53 | <h2 style="font-family:var(--font-display,sans-serif);font-size:22px;color:var(--col-text);margin:0 0 8px">Tracking schema not installed yet</h2> | |
| 54 | <p>Run the tracking migrations from <code>installation_instructions.html</code> to start collecting visitor data.</p> | |
| 55 | </div> | |
| 56 | [/if] | |
| 57 | ||
| 58 | [if:$schema_ready] | |
| 59 | <div class="at-tabs"> | |
| 60 | <a class="at-tab[if:$tab_overview] is-active[/if]" href="/admin_traffic.cgi?tab=overview">Overview</a> | |
| 61 | <a class="at-tab[if:$tab_audience] is-active[/if]" href="/admin_traffic.cgi?tab=audience">Audience</a> | |
| 62 | <a class="at-tab[if:$tab_sources] is-active[/if]" href="/admin_traffic.cgi?tab=sources">Sources</a> | |
| 63 | <a class="at-tab[if:$tab_heatmaps] is-active[/if]" href="/admin_traffic.cgi?tab=heatmaps">Heatmaps</a> | |
| 64 | <a class="at-tab[if:$tab_live] is-active[/if]" href="/admin_traffic.cgi?tab=live">Live</a> | |
| 65 | </div> | |
| 66 | ||
| 67 | [if:$tab_overview] | |
| 68 | <div class="at-kpi-grid"> | |
| 69 | <div class="at-kpi"><div class="at-kpi-label">Live now</div><div class="at-kpi-value">$kpi_live_now</div></div> | |
| 70 | <div class="at-kpi"><div class="at-kpi-label">Visitors today</div><div class="at-kpi-value">$kpi_visitors_today</div></div> | |
| 71 | <div class="at-kpi"><div class="at-kpi-label">Visitors MTD</div><div class="at-kpi-value">$kpi_visitors_mtd</div></div> | |
| 72 | <div class="at-kpi"><div class="at-kpi-label">Page views MTD</div><div class="at-kpi-value">$kpi_views_mtd</div></div> | |
| 73 | <div class="at-kpi"><div class="at-kpi-label">Unique (30d)</div><div class="at-kpi-value">$kpi_unique_30d</div></div> | |
| 74 | <div class="at-kpi"><div class="at-kpi-label">Countries</div><div class="at-kpi-value">$kpi_countries</div></div> | |
| 75 | </div> | |
| 76 | <div class="at-two-col"> | |
| 77 | <div class="at-card"> | |
| 78 | <div class="at-card-title">Top countries</div> | |
| 79 | [if:$has_country_top]<table class="at-table"><thead><tr><th>Country</th><th>Visitors</th><th></th></tr></thead><tbody>[loop:@country_top]<tr><td><strong style="color:var(--col-text)">$loop1.name</strong> <span style="color:var(--col-text-3)">$loop1.code</span></td><td>$loop1.count</td><td style="text-align:right;color:var(--col-text-3)">$loop1.pct%</td></tr>[/loop]</tbody></table>[/if] | |
| 80 | [if:!$has_country_top]<div style="color:var(--col-text-3);text-align:center;padding:24px">No data</div>[/if] | |
| 81 | </div> | |
| 82 | <div class="at-card"> | |
| 83 | <div class="at-card-title">Top pages (7d)</div> | |
| 84 | [if:$has_top_pages]<table class="at-table"><thead><tr><th>Path</th><th>Views</th></tr></thead><tbody>[loop:@top_pages]<tr><td style="font-family:var(--font-mono,monospace);font-size:12px;color:var(--col-text)">$loop1.path</td><td><strong>$loop1.views</strong></td></tr>[/loop]</tbody></table>[/if] | |
| 85 | [if:!$has_top_pages]<div style="color:var(--col-text-3);text-align:center;padding:24px">No data</div>[/if] | |
| 86 | </div> | |
| 87 | </div> | |
| 88 | [/if] | |
| 89 | ||
| 90 | [if:$tab_audience] | |
| 91 | <div class="at-three-col"> | |
| 92 | <div class="at-card"><div class="at-card-title">Browser</div>[if:$has_br_browser]<table class="at-table"><tbody>[loop:@br_browser]<tr><td>$loop1.label</td><td style="text-align:right"><span class="at-bar"><span class="at-bar-fill" style="width:$loop1.pct%"></span></span> $loop1.pct%</td></tr>[/loop]</tbody></table>[/if]</div> | |
| 93 | <div class="at-card"><div class="at-card-title">Operating system</div>[if:$has_br_os]<table class="at-table"><tbody>[loop:@br_os]<tr><td>$loop1.label</td><td style="text-align:right"><span class="at-bar"><span class="at-bar-fill" style="width:$loop1.pct%"></span></span> $loop1.pct%</td></tr>[/loop]</tbody></table>[/if]</div> | |
| 94 | <div class="at-card"><div class="at-card-title">Device</div>[if:$has_br_device]<table class="at-table"><tbody>[loop:@br_device]<tr><td>$loop1.label</td><td style="text-align:right"><span class="at-bar"><span class="at-bar-fill" style="width:$loop1.pct%"></span></span> $loop1.pct%</td></tr>[/loop]</tbody></table>[/if]</div> | |
| 95 | <div class="at-card"><div class="at-card-title">Language</div>[if:$has_br_lang]<table class="at-table"><tbody>[loop:@br_lang]<tr><td>$loop1.label</td><td style="text-align:right"><span class="at-bar"><span class="at-bar-fill" style="width:$loop1.pct%"></span></span> $loop1.pct%</td></tr>[/loop]</tbody></table>[/if]</div> | |
| 96 | </div> | |
| 97 | [/if] | |
| 98 | ||
| 99 | [if:$tab_sources] | |
| 100 | <div class="at-card"> | |
| 101 | <div class="at-card-title">Traffic sources — platform-wide</div> | |
| 102 | <div class="at-card-sub"> | |
| 103 | Where visitors across every site came from. Empty referrers (typed-in / bookmarked) bucket as <strong>(Direct / Typed-in)</strong>. | |
| 104 | </div> | |
| 105 | ||
| 106 | <div style="display:flex;gap:6px;flex-wrap:wrap;margin:14px 0 6px"> | |
| 107 | <a class="at-tab[if:$src_window_7] is-active[/if]" href="/admin_traffic.cgi?tab=sources&window=7">Last 7 days</a> | |
| 108 | <a class="at-tab[if:$src_window_30] is-active[/if]" href="/admin_traffic.cgi?tab=sources&window=30">Last 30 days</a> | |
| 109 | <a class="at-tab[if:$src_window_90] is-active[/if]" href="/admin_traffic.cgi?tab=sources&window=90">Last 90 days</a> | |
| 110 | <a class="at-tab[if:$src_window_all] is-active[/if]" href="/admin_traffic.cgi?tab=sources&window=0">All time</a> | |
| 111 | </div> | |
| 112 | ||
| 113 | [if:$has_src_rows] | |
| 114 | <div style="color:var(--col-text-3);font-size:13px;margin:8px 0 12px"> | |
| 115 | Showing $src_showing_from\–$src_showing_to of $src_total_rows domains | |
| 116 | · $src_total_sessions total sessions in window | |
| 117 | </div> | |
| 118 | <table class="at-table"> | |
| 119 | <thead><tr><th style="width:60%">Referring domain</th><th>Sessions</th><th style="text-align:right">Share</th></tr></thead> | |
| 120 | <tbody> | |
| 121 | [loop:@src_rows] | |
| 122 | <tr> | |
| 123 | <td><strong style="color:var(--col-text)">$loop1.domain</strong></td> | |
| 124 | <td>$loop1.sessions</td> | |
| 125 | <td style="text-align:right"> | |
| 126 | <span class="at-bar"><span class="at-bar-fill" style="width:$loop1.pct%"></span></span> | |
| 127 | $loop1.pct% | |
| 128 | </td> | |
| 129 | </tr> | |
| 130 | [/loop] | |
| 131 | </tbody> | |
| 132 | </table> | |
| 133 | ||
| 134 | [if:$has_src_pages_nav] | |
| 135 | <div style="display:flex;gap:6px;align-items:center;justify-content:center;margin-top:18px;flex-wrap:wrap"> | |
| 136 | [if:$src_has_prev]<a class="at-tab" href="$src_prev_href">← Prev</a>[/if] | |
| 137 | [loop:@src_pages_nav] | |
| 138 | <a class="at-tab[if:$loop1.is_current] is-active[/if]" href="$loop1.href">$loop1.num</a> | |
| 139 | [/loop] | |
| 140 | [if:$src_has_next]<a class="at-tab" href="$src_next_href">Next →</a>[/if] | |
| 141 | </div> | |
| 142 | [/if] | |
| 143 | [/if] | |
| 144 | ||
| 145 | [if:!$has_src_rows] | |
| 146 | <div style="color:var(--col-text-3);text-align:center;padding:30px">No referring traffic in this window yet.</div> | |
| 147 | [/if] | |
| 148 | </div> | |
| 149 | [/if] | |
| 150 | ||
| 151 | [if:$tab_heatmaps] | |
| 152 | <div class="at-card"> | |
| 153 | <div class="at-card-title">Top pages by clicks (30d) — platform-wide</div> | |
| 154 | <div class="at-card-sub">Every page across every site, ranked by raw click count.</div> | |
| 155 | [if:$has_hm_top_pages] | |
| 156 | <table class="at-table"> | |
| 157 | <thead><tr><th>Path</th><th>Clicks</th><th>Sessions</th></tr></thead> | |
| 158 | <tbody>[loop:@hm_top_pages]<tr><td style="font-family:var(--font-mono,monospace);font-size:12px;color:var(--col-text)">$loop1.path_safe</td><td><strong>$loop1.clicks</strong></td><td>$loop1.sessions</td></tr>[/loop]</tbody> | |
| 159 | </table> | |
| 160 | [/if] | |
| 161 | [if:!$has_hm_top_pages]<div style="color:var(--col-text-3);text-align:center;padding:30px">No heatmap data yet.</div>[/if] | |
| 162 | </div> | |
| 163 | [/if] | |
| 164 | ||
| 165 | [if:$tab_live] | |
| 166 | <div class="at-card"> | |
| 167 | <div class="at-card-title">Live visitors across all sites</div> | |
| 168 | <div class="at-card-sub">Page auto-refreshes every 15 seconds.</div> | |
| 169 | [if:$has_visitors] | |
| 170 | <div>[loop:@visitors] | |
| 171 | <div class="at-visitor-row"> | |
| 172 | <span class="at-dot[if:$loop1.is_active] active[/if]"></span> | |
| 173 | <div style="flex:1;min-width:0"> | |
| 174 | <div style="font-size:13.5px;color:var(--col-text);font-weight:600">$loop1.label <span style="color:var(--col-text-3);font-weight:400">on $loop1.site</span></div> | |
| 175 | <div class="at-visitor-meta">$loop1.browser_name on $loop1.os_name · $loop1.device_type[if:$loop1.country] · $loop1.country[/if]</div> | |
| 176 | </div> | |
| 177 | <div class="at-visitor-meta" style="text-align:right"> | |
| 178 | <div style="font-family:var(--font-mono,monospace);color:var(--col-accent-bright)">$loop1.current_page</div> | |
| 179 | <div>$loop1.idle_label</div> | |
| 180 | </div> | |
| 181 | </div> | |
| 182 | [/loop]</div> | |
| 183 | [/if] | |
| 184 | [if:!$has_visitors]<div style="color:var(--col-text-3);text-align:center;padding:30px">No active sessions right now.</div>[/if] | |
| 185 | </div> | |
| 186 | <script>setTimeout(function(){ window.location.reload(); }, 15000);</script> | |
| 187 | [/if] | |
| 188 | [/if] | |
| 189 | <div class="at-foot-spacer" data-end="admin-traffic"> </div> |