mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
Publish the resolved cycle verdict instead of letting each client work it out
The cycle ring's day count, its phase arcs, and the judgement that a period is late all used to be worked out in web client code, including the fourteen-day grace window that decides when the count stops being an observed fact. Anything else that wanted the same answer had to reproduce that rule, and a second copy of it is how a ring ends up showing someone a cycle day their record does not hold. GET /api/cycle/calendar now carries a verdict: the state, the cycle day, the ring's arcs, the cycle start, how many days until the next period, and when the period is late, how late. It is resolved against the user's own timezone day, which a browser in another zone cannot do. The web wheel, the wellness-strip tile, the insights teaser and the temperature chart all render it, the grace constant is gone from the components directory, and the spec says the field is required. The overdue caption now names the number of days instead of only saying later than usual.
This commit is contained in:
1 parent
6c87c96fa6
commit
dbecbbcbdb
21 files changed
+702
-554
No files matched your search
@@ -30,6 +30,7 @@ import {
|
||||
goalAllowsFertileWindow,
|
||||
cycleDisclaimerKey,
|
||||
} from "@/lib/cycle/dto";
|
||||
import { resolveCycleVerdict } from "@/lib/cycle/verdict";
|
||||
import { persistPredictionCache } from "@/lib/cycle/prediction-cache";
|
||||
import { addDays, dayDiff } from "@/lib/cycle/day-math";
|
||||
import { BBT_WINDOW } from "@/lib/cycle/types";
|
||||
@@ -173,12 +174,36 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
// "not a contraceptive method" caveat (QA H-1).
|
||||
const disclaimer = t.t(cycleDisclaimerKey(profile.goal));
|
||||
|
||||
const predictionDto = prediction
|
||||
? toCyclePredictionDTO(prediction, goalAllowsFertile, disclaimer)
|
||||
: null;
|
||||
|
||||
// The resolved verdict. Built from the SAME grid the response carries and
|
||||
// from the ALREADY-GATED prediction DTO, never the raw engine result — a
|
||||
// fertile window suppressed for the profile's goal or by the cold-start gate
|
||||
// cannot reappear here. `today` is the user's timezone day, which is the
|
||||
// other half of why this cannot be a client-side derivation: a browser in a
|
||||
// different zone resolves a different "today" and reads a different cycle day.
|
||||
const verdict = resolveCycleVerdict({
|
||||
days,
|
||||
today,
|
||||
profile: {
|
||||
typicalCycleLength: profile.typicalCycleLength,
|
||||
typicalPeriodLength: profile.typicalPeriodLength,
|
||||
lutealPhaseLength: profile.lutealPhaseLength,
|
||||
},
|
||||
nextPeriodStart: predictionDto?.nextPeriodStart ?? null,
|
||||
fertileWindowStart: predictionDto?.fertileWindowStart ?? null,
|
||||
fertileWindowEnd: predictionDto?.fertileWindowEnd ?? null,
|
||||
});
|
||||
|
||||
annotate({
|
||||
action: { name: "cycle.calendar.read" },
|
||||
meta: {
|
||||
days: days.length,
|
||||
cycles_observed: prediction?.cyclesObserved ?? 0,
|
||||
has_prediction: prediction !== null,
|
||||
verdict_state: verdict.state,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -194,9 +219,11 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
// so a consumer never sees two divergent "cycles observed" values.
|
||||
cyclesObserved: prediction?.cyclesObserved ?? cycles.length,
|
||||
},
|
||||
prediction: prediction
|
||||
? toCyclePredictionDTO(prediction, goalAllowsFertile, disclaimer)
|
||||
: null,
|
||||
prediction: predictionDto,
|
||||
// The one resolved answer about today: cycle day, the ring's arcs, how
|
||||
// many days until the next period, and whether the period is late and by
|
||||
// how much. Every client renders this; none of them recomputes it.
|
||||
verdict,
|
||||
// Top-level cold-start flag (mirrors `prediction.stillLearning`): the
|
||||
// calendar grid has suppressed fertile/ovulation/phase output, so the
|
||||
// client renders a calm "learning your cycle" banner over the grid. Additive
|
||||
|
||||
Reference in new issue
Block a user