feat: add WAU history table, fix retention metric, add new logos and favicon

- Add 12-week Weekly Active Users table to global stats with WoW change %
- Fix 7-day and 30-day retention to measure return on exactly day N (not any day within the window)
- Remove "Avg Guesses Today" stat card
- Update retention description to clarify exact-day measurement
- Add bibdle logos (SVG, square PNG, circle PNG) and new favicon.png
- Wire favicon.png as the site favicon via app.html link tag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
George Powell
2026-03-16 00:04:45 -04:00
parent e878dea235
commit 83cfcc66c0
7 changed files with 1066 additions and 329 deletions

View File

@@ -285,11 +285,8 @@ export const load: PageServerLoad = async () => {
if (!cohort || cohort.size < 3) continue; // skip tiny cohorts
let retained = 0;
for (const userId of cohort) {
for (let j = 1; j <= windowDays; j++) {
if (dateUsersMap.get(addDays(dateD, j))?.has(userId)) {
retained++;
break;
}
if (dateUsersMap.get(addDays(dateD, windowDays))?.has(userId)) {
retained++;
}
}
series.push({
@@ -304,6 +301,32 @@ export const load: PageServerLoad = async () => {
const retention7dSeries = retentionSeries(7, 30);
const retention30dSeries = retentionSeries(30, 30);
// ── Weekly Active Users history (12 weeks) ────────────────────────────────
const wauWeeks: { weekStart: string; weekEnd: string; wau: number; changePct: number | null }[] = [];
for (let w = 0; w < 12; w++) {
const weekEnd = estDateStr(w * 7);
const weekStart = estDateStr(w * 7 + 6);
const users = new Set<string>();
for (const row of recentCompletions) {
if (row.date >= weekStart && row.date <= weekEnd) {
users.add(row.anonymousId);
}
}
wauWeeks.push({ weekEnd, weekStart, wau: users.size, changePct: null });
}
// Change % vs prior week (index i+1 is the older week)
for (let i = 0; i < wauWeeks.length - 1; i++) {
const prev = wauWeeks[i + 1].wau;
if (prev > 0) {
wauWeeks[i].changePct = Math.round(((wauWeeks[i].wau - prev) / prev) * 1000) / 10;
}
}
const avgWau = Math.round(wauWeeks.reduce((sum, w) => sum + w.wau, 0) / wauWeeks.length);
return {
todayEst,
stats: {
@@ -335,6 +358,8 @@ export const load: PageServerLoad = async () => {
current7dAvg: current7dReturnAvg,
prior7dAvg: prior7dReturnAvg,
change: returnRateChange
}
},
wauWeeks,
avgWau
};
};