Skip to content

Commit 11630ed

Browse files
committed
Add traffic dashboard (Pages)
1 parent bcc1dc5 commit 11630ed

1 file changed

Lines changed: 100 additions & 0 deletions

File tree

docs/index.html

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>PythonLearningNotes — Traffic Dashboard</title>
7+
<style>
8+
:root {
9+
--bg: #0d1117; --card: #161b22; --border: #30363d;
10+
--text: #e6edf3; --text2: #8b949e; --blue: #58a6ff;
11+
--green: #3fb950; --orange: #d29922; --purple: #bc8cff;
12+
}
13+
* { box-sizing: border-box; margin: 0; padding: 0; }
14+
body { background: var(--bg); color: var(--text); font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif; line-height: 1.6; }
15+
.container { max-width: 800px; margin: 0 auto; padding: 2rem 1.5rem; }
16+
h1 { font-size: 1.5rem; margin-bottom: .25rem; }
17+
.subtitle { color: var(--text2); font-size: .9rem; margin-bottom: 1.5rem; }
18+
.subtitle a { color: var(--blue); }
19+
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: .75rem; margin-bottom: 2rem; }
20+
.metric { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 1rem; text-align: center; }
21+
.metric .val { font-size: 1.5rem; font-weight: 700; }
22+
.metric .lbl { color: var(--text2); font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; }
23+
.section { margin-bottom: 2rem; }
24+
.section h2 { font-size: 1.1rem; margin-bottom: .75rem; }
25+
table { width: 100%; border-collapse: collapse; background: var(--card); border-radius: 8px; overflow: hidden; }
26+
th, td { padding: .6rem .75rem; text-align: left; border-bottom: 1px solid var(--border); font-size: .9rem; }
27+
th { background: var(--bg); color: var(--text2); font-weight: 600; text-transform: uppercase; font-size: .75rem; letter-spacing: .05em; }
28+
tr:last-child td { border-bottom: none; }
29+
.note { color: var(--text2); font-size: .8rem; margin-top: 2rem; text-align: center; }
30+
.val-blue { color: var(--blue); }
31+
.val-green { color: var(--green); }
32+
.val-purple { color: var(--purple); }
33+
.val-orange { color: var(--orange); }
34+
.bars { display: flex; align-items: flex-end; gap: 4px; height: 120px; padding: 1rem 0; }
35+
.bar { flex: 1; background: var(--blue); border-radius: 3px 3px 0 0; min-height: 2px; position: relative; opacity: .8; }
36+
.bar:hover { opacity: 1; }
37+
.bar-label { position: absolute; top: -1.2rem; left: 50%; transform: translateX(-50%); font-size: .65rem; color: var(--text2); white-space: nowrap; }
38+
@media (max-width: 600px) { .grid { grid-template-columns: repeat(2, 1fr); } }
39+
</style>
40+
</head>
41+
<body>
42+
<div class="container">
43+
<h1>📊 <a href="https://github.com/B67687/PythonLearningNotes" style="color:var(--text);text-decoration:none">PythonLearningNotes</a></h1>
44+
<p class="subtitle"><a href="https://github.com/B67687/PythonLearningNotes">Repository</a> · Traffic data from GitHub API</p>
45+
<div class="grid" id="metrics"></div>
46+
<div class="section">
47+
<h2>Last 7 Days</h2>
48+
<div class="bars" id="bars"></div>
49+
</div>
50+
<div class="section">
51+
<h2>Referrers</h2>
52+
<table><thead><tr><th>Source</th><th>Count</th><th>Uniques</th></tr></thead><tbody id="refs"></tbody></table>
53+
</div>
54+
<div class="section">
55+
<h2>Popular Paths</h2>
56+
<table><thead><tr><th>Path</th><th>Count</th><th>Uniques</th></tr></thead><tbody id="paths"></tbody></table>
57+
</div>
58+
<div class="note">
59+
Data updates daily at 3am UTC · <a href="https://gist.github.com/B67687/3681a13789d9a7551c0998945dd5804e" style="color:var(--blue)">Raw data gist</a>
60+
</div>
61+
</div>
62+
<script>
63+
fetch('https://gist.githubusercontent.com/B67687/3681a13789d9a7551c0998945dd5804e/raw/state.json')
64+
.then(r => { if (!r.ok) throw new Error('no data yet'); return r.json(); })
65+
.then(s => {
66+
const org = s.totalOrganicClones ?? s.totalClones ?? 0;
67+
document.getElementById('metrics').innerHTML = `
68+
<div class="metric"><div class="val val-blue">${org.toLocaleString()}</div><div class="lbl">Organic Clones</div></div>
69+
<div class="metric"><div class="val val-purple">${(s.totalUniqueClones ?? 0).toLocaleString()}</div><div class="lbl">Unique Cloners</div></div>
70+
<div class="metric"><div class="val val-green">${(s.totalViews ?? 0).toLocaleString()}</div><div class="lbl">Views</div></div>
71+
<div class="metric"><div class="val val-orange">${(s.totalUniqueViews ?? 0).toLocaleString()}</div><div class="lbl">Unique Viewers</div></div>
72+
<div class="metric"><div class="val">${(s.totalDownloads ?? 0).toLocaleString()}</div><div class="lbl">Downloads</div></div>
73+
<div class="metric"><div class="val">${s.stars ?? 0}</div><div class="lbl">★ Stars</div></div>
74+
`;
75+
const hist = (s.dailyHistory || []).slice(-7);
76+
const bars = document.getElementById('bars');
77+
const max = Math.max(...hist.map(d => (d.organicClones ?? d.clones ?? 0) + (d.downloads ?? 0)), 1);
78+
bars.innerHTML = hist.map(d => {
79+
const v = (d.organicClones ?? d.clones ?? 0) + (d.downloads ?? 0);
80+
const pct = (v / max * 100) || 2;
81+
const lbl = d.date.slice(5, 10);
82+
return `<div class="bar" style="height:${pct}%" title="${v} installs"><span class="bar-label">${lbl}</span></div>`;
83+
}).join('');
84+
const refs = document.getElementById('refs');
85+
(s.referrers || []).slice(0, 10).forEach(r => {
86+
refs.innerHTML += `<tr><td>${r.source}</td><td>${r.count}</td><td>${r.uniques}</td></tr>`;
87+
});
88+
if (!refs.innerHTML) refs.innerHTML = '<tr><td colspan="3" style="color:var(--text2)">No referrer data yet</td></tr>';
89+
const paths = document.getElementById('paths');
90+
(s.popularPaths || []).slice(0, 10).forEach(p => {
91+
paths.innerHTML += `<tr><td>${p.title || p.path}</td><td>${p.count}</td><td>${p.uniques}</td></tr>`;
92+
});
93+
if (!paths.innerHTML) paths.innerHTML = '<tr><td colspan="3" style="color:var(--text2)">No path data yet</td></tr>';
94+
})
95+
.catch(e => {
96+
document.getElementById('metrics').innerHTML = `<p style="color:var(--text2);grid-column:1/-1">No traffic data yet. Workflow runs daily at 3am UTC.</p>`;
97+
});
98+
</script>
99+
</body>
100+
</html>

0 commit comments

Comments
 (0)