Area and bar fills at 0.6 opacity (were a near-invisible 0.15); the day
view's 'Last 24 hours' legend entry uses the same full-opacity accent
line as the other entries instead of a translucent bar swatch.
Port the seasonal.py demo algorithm to JS (analytics/seasonal.js): the
smoothed 5-minute history is folded onto a weekly grid with exponential
age decay (7-day half-life for the time-of-day pattern, 42 days for
per-weekday deviations, shrunk by effective weeks of data), capped at
180 days of history where the weights become negligible.
Week view: the fading past-week overlays are replaced by the full
Monday-first 'Typical week' estimate. Day view gains the same estimate
cut to the rolling 24h window as a smooth muted 'Typical <weekday>'
curve behind the bars. Longer ranges are unchanged.
Past-week overlays can run far above the autoscaled y range, and the svg
is overflow: visible for the axis labels — wrap the data paths (bars,
skyline, area/line curves) in a clipped group so they cannot paint outside
the plot rect.
- Charts grow to a larger intrinsic size (1052x174) and never upscale
past it; centered with equal side margins above the cap, full width
below, svg always within page bounds; overflow visible so wider fonts
don't clip at the viewBox edge
- Totals row aligns its left edge with the charts and shrinks (gap first,
then font) via container units to always stay on one line
- Unified chart text at 11px system-ui; fixed size independent of theme font
- Day view y axis reads "visits / 5 min" / "views / 5 min"
- Left margin and y tick spacing tightened (MARGIN_L 56 -> 40)
- Gap between visits and views charts removed
- Legend repositioned for the larger font
- weeklySeries shifts overlaid weeks onto the current week's time axis so
they overlay inside the plot instead of overflowing left; oldest weeks
paint first, current week on top
- Legend moved inside the visits chart's top right: current ISO week in
accent, past weeks as a single muted "Week M" / "Week M–N" specimen
- Past week curves use the muted color instead of faded accent
- Chart height reduced ~30% (180 -> 126)
- Charts render as single SVGs with axis labels inside the viewBox,
replacing the stretched plot + HTML overlay labels
- Rolling ranges end at now, t0 aligned to UTC day; bucket size follows
the window (6h up to 31 days) so "all" at its 30-day minimum renders
identically to "month"
- X labels always centered on their true position; no edge-align shifting
- rangeWindow simplified to rolling spans ending at now
- TransitionGraph "all" visual scale floored at the 30-day plot minimum
Add server-side visit analytics collection, a public-page ping endpoint,
and a full-screen AnalyticsView for admins.
Backend:
- Add pagerite/analytics.py: Analytics/Visit model, Store, and persistence
- Wire /_a ping endpoint and GET /_api/analytics into pagerite/app.py
Frontend:
- Add full-screen AnalyticsView with visitor charts and transition map
- Add VisitorCharts and TransitionGraph subcomponents
- Add analytics JS helpers in frontend/src/analytics/
- Send navigation pings from frontend/src/pagerite.js
- Mount AnalyticsView from frontend/src/main.js
- Document the feature in docs/analytics.md and update AGENTS.md