Skip to main content

DASHBOARD_HTML

Constant DASHBOARD_HTML 

Source
pub const DASHBOARD_HTML: &str = r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Analytics - Rush Sync Server</title>
<link rel="icon" href="/.rss/favicon.svg" type="image/svg+xml">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#0a0a0f;color:#e4e4ef;min-height:100vh}
.container{max-width:1200px;margin:0 auto;padding:24px}
.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}
.header h1{font-size:24px;font-weight:700;letter-spacing:-0.5px}
.header h1 span{color:#6c63ff}
.back{color:#6c63ff;text-decoration:none;font-size:14px}
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-bottom:24px}
.card{background:#14141f;border:1px solid #2a2a3a;border-radius:12px;padding:20px}
.card .lbl{font-size:12px;color:#8888a0;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:8px}
.card .val{font-size:36px;font-weight:700}
.card .val.purple{color:#6c63ff}
.card .val.green{color:#00d4aa}
.card .val.blue{color:#00a8ff}
.tabs{display:flex;gap:8px;margin-bottom:16px}
.tab{padding:8px 16px;border-radius:8px;background:#14141f;border:1px solid #2a2a3a;color:#8888a0;cursor:pointer;font-size:13px;transition:all 0.2s}
.tab:hover{border-color:#6c63ff}
.tab.active{background:#6c63ff;color:#fff;border-color:#6c63ff}
.section{background:#14141f;border:1px solid #2a2a3a;border-radius:12px;padding:20px;margin-bottom:16px}
.section h2{font-size:15px;margin-bottom:16px;font-weight:600;color:#c0c0d0}
.chart{display:flex;align-items:flex-end;gap:2px;height:140px;padding-bottom:24px;position:relative}
.bar-w{flex:1;display:flex;flex-direction:column;align-items:center;position:relative}
.bar{width:100%;background:linear-gradient(180deg,#6c63ff,#4a43cc);border-radius:3px 3px 0 0;min-height:2px;transition:height 0.3s;cursor:pointer}
.bar:hover{background:linear-gradient(180deg,#8b83ff,#6c63ff)}
.bar-lbl{font-size:8px;color:#8888a0;position:absolute;bottom:-20px;white-space:nowrap}
.tooltip{display:none;position:absolute;top:-30px;background:#2a2a3a;color:#e4e4ef;padding:4px 8px;border-radius:4px;font-size:11px;white-space:nowrap;z-index:10}
.bar:hover+.tooltip{display:block}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}
@media(max-width:768px){.grid{grid-template-columns:1fr}.cards{grid-template-columns:1fr 1fr}}
table{width:100%;border-collapse:collapse}
th{text-align:left;font-size:11px;color:#8888a0;text-transform:uppercase;letter-spacing:0.5px;padding:8px 0;border-bottom:1px solid #2a2a3a}
td{padding:8px 0;font-size:13px;border-bottom:1px solid #1a1a2a}
td:last-child{text-align:right;font-weight:600;color:#6c63ff}
.sub-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px}
.sub-card{background:#1a1a2a;border-radius:8px;padding:16px}
.sub-card .name{font-weight:600;margin-bottom:4px;color:#e4e4ef}
.sub-card .stats{font-size:12px;color:#8888a0}
.empty{color:#555;font-style:italic;font-size:13px;padding:20px;text-align:center}
.footer{text-align:center;font-size:11px;color:#555;padding:16px}
</style>
</head>
<body>
<div class="container">
<div class="header"><h1>Analytics <span>Dashboard</span></h1><a href="/" class="back">&larr; Back</a></div>
<div class="cards" id="cards"></div>
<div class="tabs" id="tabs">
<div class="tab active" data-p="today">Today</div>
<div class="tab" data-p="last_7_days">7 Days</div>
<div class="tab" data-p="last_30_days">30 Days</div>
</div>
<div class="section"><h2>Hourly Traffic (Last 24h)</h2><div class="chart" id="chart"></div></div>
<div class="grid">
<div class="section"><h2>Top Pages</h2><div id="pages"></div></div>
<div class="section"><h2>Top Downloads</h2><div id="downloads"></div></div>
</div>
<div class="section"><h2>By Subdomain</h2><div class="sub-grid" id="subs"></div></div>
<div class="footer" id="foot">Loading...</div>
</div>
<script>
var D=__ANALYTICS_DATA__;
var P='today';
document.querySelectorAll('.tab').forEach(function(t){t.addEventListener('click',function(){document.querySelectorAll('.tab').forEach(function(x){x.classList.remove('active')});t.classList.add('active');P=t.dataset.p;render()})});
function render(){var p=D[P]||D.today||{};
document.getElementById('cards').innerHTML='<div class="card"><div class="lbl">Page Views</div><div class="val purple">'+fmt(p.page_views)+'</div></div>'+'<div class="card"><div class="lbl">Unique Visitors</div><div class="val green">'+fmt(p.unique_visitors)+'</div></div>'+'<div class="card"><div class="lbl">Downloads</div><div class="val blue">'+fmt(p.downloads)+'</div></div>';
var h=D.hourly_traffic||[];
if(h.length===0){document.getElementById('chart').innerHTML='<div class="empty">No hourly data yet</div>'}
else{var mx=Math.max.apply(null,h.map(function(x){return x.views}))||1;document.getElementById('chart').innerHTML=h.map(function(x){var pct=Math.max((x.views/mx)*100,2);var hr=(x.hour.split('T')[1]||'').replace(':00','h');return '<div class="bar-w"><div class="bar" style="height:'+pct+'%"></div><div class="tooltip">'+x.views+' views, '+x.unique+' unique</div><div class="bar-lbl">'+hr+'</div></div>'}).join('')}
var pg=p.top_pages||[];
if(pg.length===0){document.getElementById('pages').innerHTML='<div class="empty">No page views yet</div>'}
else{document.getElementById('pages').innerHTML='<table><tr><th>Page</th><th>Views</th></tr>'+pg.map(function(x){return '<tr><td>'+esc(x.path)+'</td><td>'+fmt(x.views)+'</td></tr>'}).join('')+'</table>'}
var dl=p.top_downloads||[];
if(dl.length===0){document.getElementById('downloads').innerHTML='<div class="empty">No downloads yet</div>'}
else{document.getElementById('downloads').innerHTML='<table><tr><th>File</th><th>Count</th></tr>'+dl.map(function(x){return '<tr><td>'+esc(x.file)+'</td><td>'+fmt(x.count)+'</td></tr>'}).join('')+'</table>'}
var sb=D.by_subdomain||{};var sk=Object.keys(sb);
if(sk.length===0){document.getElementById('subs').innerHTML='<div class="empty">No subdomain data yet</div>'}
else{document.getElementById('subs').innerHTML=sk.map(function(s){return '<div class="sub-card"><div class="name">'+esc(s)+'</div><div class="stats">'+fmt(sb[s].views)+' views &middot; '+fmt(sb[s].unique)+' unique</div></div>'}).join('')}
document.getElementById('foot').textContent='Last updated: '+new Date().toLocaleTimeString()+' \u00b7 Auto-refresh in 30s'}
function fmt(n){return (n||0).toLocaleString()}
function esc(s){var d=document.createElement('div');d.textContent=s;return d.innerHTML}
render();setTimeout(function(){location.reload()},30000);
</script>
</body></html>"#;
Expand description

Dashboard HTML template. The placeholder __ANALYTICS_DATA__ is replaced with the current analytics JSON at render time.