diff options
Diffstat (limited to 'src/lib/shared/routine-metrics.ts')
| -rw-r--r-- | src/lib/shared/routine-metrics.ts | 21 |
1 files changed, 16 insertions, 5 deletions
diff --git a/src/lib/shared/routine-metrics.ts b/src/lib/shared/routine-metrics.ts index 953a025..7758f27 100644 --- a/src/lib/shared/routine-metrics.ts +++ b/src/lib/shared/routine-metrics.ts @@ -92,10 +92,12 @@ export function computeRoutineState( const period = periodMs(routine.frequencyDays); // Calendar-day anchor: the period starts at midnight of the reference day, // so any new calendar day past the frequency flips the routine to due. + // A never-done routine has no completed period: its creation day is its + // due date, so it ages exactly like a done one whose anchor is creation. const due = reference .toZonedDateTimeISO(UTC) .startOfDay() - .add({ days: routine.frequencyDays }) + .add({ days: lastDone === null ? 0 : routine.frequencyDays }) .toInstant(); const rawRatio = (now.epochMilliseconds - due.epochMilliseconds) / period; const overdueRatio = Math.max(0, rawRatio); @@ -141,11 +143,12 @@ function healthAtPoint( reference: Temporal.Instant, frequencyDays: number, t: Temporal.Instant, + neverDone = false, ): number { const due = reference .toZonedDateTimeISO(UTC) .startOfDay() - .add({ days: frequencyDays }) + .add({ days: neverDone ? 0 : frequencyDays }) .toInstant(); const rawRatio = (t.epochMilliseconds - due.epochMilliseconds) / periodMs(frequencyDays); @@ -156,7 +159,8 @@ function healthAtPoint( * Daily health samples from the routine's creation to now: 100 right after a * completion, then a linear decay to 0 once two full periods are overdue * (sawtooth). - * Each sample anchors on the latest completion at or before it (or creation). + * Each sample anchors on the latest completion at or before it (or creation, + * which is then itself the due date for a never-done routine). * Uses the routine's current frequency across the whole history. */ export function computeHealthSeries( @@ -183,7 +187,12 @@ export function computeHealthSeries( reference = doneAt[nextCompletion]; nextCompletion++; } - return healthAtPoint(reference, routine.frequencyDays, t); + return healthAtPoint( + reference, + routine.frequencyDays, + t, + nextCompletion === 0, + ); }; for ( @@ -250,7 +259,9 @@ export function computeProjectHealthSeries( ); const anchor = doneAt.length > 0 ? (maxInstant(doneAt) ?? now) : createdOrNow(r); - return sum + healthAtPoint(anchor, r.frequencyDays, t); + return ( + sum + healthAtPoint(anchor, r.frequencyDays, t, doneAt.length === 0) + ); }, 0); return Math.round(total / alive.length); }; |
