From dccfc326720a6d181bbc62d22e3e5595e7c8b037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81sgeir=20Thor=20Johnson?= Date: Sun, 21 Jun 2026 14:28:51 +0000 Subject: [PATCH] Add per-page history drill-down to traffic dashboard Click any row in the Top Pages table to see that page's full history: - Area chart of 14-day rolling views + uniques over time - Data table with every snapshot date and day-over-day change badges - Section hidden until first click, switches on subsequent clicks --- .github/scripts/accumulate-traffic.py | 56 ++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/.github/scripts/accumulate-traffic.py b/.github/scripts/accumulate-traffic.py index d0ce9a7..13525f8 100644 --- a/.github/scripts/accumulate-traffic.py +++ b/.github/scripts/accumulate-traffic.py @@ -227,6 +227,9 @@ DASHBOARD_TEMPLATE = r""" th{text-align:left;padding:6px 10px;border-bottom:2px solid var(--border);color:var(--text2);font-weight:700;font-size:.65rem;text-transform:uppercase;letter-spacing:.04em} td{padding:6px 10px;border-bottom:1px solid var(--border)} tr:last-child td{border-bottom:none} + tr.clickable{cursor:pointer;transition:background .15s} + tr.clickable:hover{background:rgba(99,102,241,.06)} + tr.clickable.active{background:rgba(99,102,241,.10)} .mono{font-family:'SF Mono','Fira Code','Cascadia Code',monospace;font-size:.75rem} .bar-wrap{position:relative} .bar-fill{position:absolute;left:0;top:2px;bottom:2px;border-radius:3px;opacity:.12} @@ -253,6 +256,8 @@ DASHBOARD_TEMPLATE = r"""
Top Pages (top 20 — peak 14-day count across all snapshots)
Top Referrers (top 20 — peak 14-day count across all snapshots)
+ +

All Referrers — peak 14-day count per source across all snapshots

Referrer Trends

@@ -444,10 +449,59 @@ function renderTopPagesTable(showAgg) { document.getElementById('pathsTable').innerHTML = '#PagePeak ViewsUnique'+ topPaths.map(([path,d],i)=>{ const w = Math.round(d.count/(topPaths[0]?.[1].count||1)*100); - return `${i+1}
${shortenPath(path)}${fmt(d.count)}${fmt(d.uniques)}`; + const esc = path.replace(/'/g,"\\'"); + return `${i+1}
${shortenPath(path)}${fmt(d.count)}${fmt(d.uniques)}`; }).join('')+''; } +let pageHistChart = null; +function showPageHistory(path) { + document.querySelectorAll('#pathsTable tr.clickable').forEach(r=>r.classList.remove('active')); + const rows = document.querySelectorAll('#pathsTable tr.clickable'); + rows.forEach(r=>{ if(r.querySelector('.mono')?.textContent===shortenPath(path)) r.classList.add('active'); }); + + const series = DATA.paths_series.map(s=>{ + const p = s.paths.find(x=>x.path===path); + return { date:s.date, count:p?p.count:null, uniques:p?p.uniques:null }; + }).filter(d=>d.count!==null); + + const section = document.getElementById('pageHistorySection'); + const card = document.getElementById('pageHistoryCard'); + section.style.display = ''; + card.style.display = ''; + document.getElementById('pageHistoryTitle').textContent = shortenPath(path) + ' — view count over time'; + + if (pageHistChart) pageHistChart.destroy(); + pageHistChart = new ApexCharts(document.getElementById('pageHistoryChart'), { + ...baseOpts, + chart:{...baseOpts.chart, type:'area', height:260, zoom:{enabled:true,type:'x',autoScaleYaxis:true}}, + series:[ + {name:'Views (14d window)', data:series.map(d=>[new Date(d.date).getTime(), d.count])}, + {name:'Unique', data:series.map(d=>[new Date(d.date).getTime(), d.uniques])}, + ], + colors:['#6366f1','#f59e0b'], + fill:{type:'gradient',gradient:{shadeIntensity:1,opacityFrom:0.4,opacityTo:0.05,stops:[0,95]}}, + stroke:{curve:'smooth',width:2.5}, + xaxis:{type:'datetime',labels:{datetimeUTC:false}}, + yaxis:{labels:{formatter:v=>v>=1000?(v/1000).toFixed(1)+'k':v}}, + dataLabels:{enabled:false}, + markers:{size:3,hover:{size:5}}, + legend:{position:'top',horizontalAlign:'left',fontSize:'12px'}, + tooltip:{shared:true,x:{format:'MMM d, yyyy'}}, + }); + pageHistChart.render(); + + const tbl = document.getElementById('pageHistoryTable'); + tbl.innerHTML = 'DateViews (14d)UniqueChange'+ + series.map((d,i)=>{ + let badge=''; + if(i>0&&series[i-1].count!=null){const delta=d.count-series[i-1].count;const pct=series[i-1].count?Math.round(delta/series[i-1].count*100):0;if(delta!==0) badge=`${delta>0?'+':''}${pct}%`;} + return `${d.date}${fmt(d.count)}${fmt(d.uniques)}${badge}`; + }).reverse().join('')+''; + + card.scrollIntoView({behavior:'smooth',block:'nearest'}); +} + const topRefs = allRefs.slice(0,20); document.getElementById('refsTable').innerHTML = '#ReferrerPeak ViewsUnique'+ topRefs.map(([ref,d],i)=>{