diff --git a/frontend/src/routes/[fam]/+page.svelte b/frontend/src/routes/[fam]/+page.svelte index 419af7a..d60771c 100644 --- a/frontend/src/routes/[fam]/+page.svelte +++ b/frontend/src/routes/[fam]/+page.svelte @@ -171,30 +171,27 @@ function todayPotentialFor(memberId: string) { return assigned.filter((a: any) => a.memberId === memberId && a.frequency === 'daily').length; } - // Max points a full "all daily chores done" day is worth for a member. - function dailyPointsPotential(memberId: string) { - return assigned - .filter((a: any) => a.memberId === memberId && a.frequency === 'daily') - .reduce((sum: number, a: any) => sum + (a.type === 'points' ? Number(a.value) || 0 : 0), 0); - } - // All-time average daily points for a member, from the server-computed - // weekly series (all history, not just this week). Days active runs from - // their earliest recorded week through today (min 1). - function avgDailyPointsFor(memberId: string): number { + // All-time completion rate for a member: all chores ever done ÷ (current + // weekly potential × weeks active). Bounded and meaningful, unlike the old + // points-per-day average (bonus points + weeklies done in one day could + // push it past a "perfect day" — e.g. Jethro's 100%). + function overallRateFor(memberId: string): { done: number; total: number; pct: number } { const line = trend.find((m: any) => m.memberId === memberId); const weeks = line?.weeks || []; - if (weeks.length === 0) return 0; - const total = weeks.reduce((sum: number, w: any) => sum + (Number(w.points) || 0), 0); + const done = weeks.reduce((sum: number, w: any) => sum + (Number(w.chores) || 0), 0); + const potential = weeklyPotentialFor(memberId); + if (weeks.length === 0 || potential <= 0) return { done, total: 0, pct: 0 }; const earliest = weeks.map((w: any) => w.weekStart).sort()[0]; const today = todayInTz(famTz); - const days = Math.max( + const weeksActive = Math.max( 1, - Math.round( + Math.floor( (new Date(today + 'T00:00:00').getTime() - new Date(earliest + 'T00:00:00').getTime()) / - 86400000 + (7 * 86400000) ) + 1 ); - return total / days; + const total = potential * weeksActive; + return { done, total, pct: Math.min(100, Math.round((done / total) * 100)) }; } // Today's completion count — tolerant of UTC vs fam-local date keys. function todayDone(s: any): number { @@ -277,10 +274,11 @@ } const scoreCanvas = donutCanvases[`${s.memberId}:score`]; if (scoreCanvas) { + const overall = overallRateFor(s.memberId); donutCharts[`${s.memberId}:score`] = makeDonut( scoreCanvas, - avgDailyPointsFor(s.memberId), - dailyPointsPotential(s.memberId), + overall.done, + overall.total, s.memberColor ); } @@ -413,9 +411,7 @@ {@const tdTotal = todayPotentialFor(s.memberId)} {@const tdDone = todayDone(s)} {@const tdPct = tdTotal > 0 ? Math.min(100, Math.round((tdDone / tdTotal) * 100)) : 0} - {@const scPot = dailyPointsPotential(s.memberId)} - {@const scVal = avgDailyPointsFor(s.memberId)} - {@const scPct = scPot > 0 ? Math.min(100, Math.round((scVal / scPot) * 100)) : 0} + {@const sc = overallRateFor(s.memberId)} {@const goals = goalsByMember.get(s.memberId)}
@@ -438,8 +434,8 @@
-
{scPct}%
- Avg/day +
{sc.pct}%
+ Overall