fix(workouts): hold training load through unscored days

A day without a stored training impulse was read as a rest day, so a
watch left off or a nightly run that had not happened yet pulled
fitness and fatigue toward zero. Such a day is now not rated: the
averages hold through it, the series marks it, and the card says how
many of the last 42 days were rated. A scored day of 0 stays rest. The
figures describe the last stored day rather than always yesterday.
This commit is contained in:
Marc-André Bombeck committed 2026-10-21 23:09:09 +02:00
1 parent 2723ac5b98
commit 99dfd68aad
15 files changed
+145 -38

No files matched your search

@@ -17,6 +17,8 @@ const OK: TrainingLoad = {
tsb: -7.6,
daysCovered: 30,
trainingDays: 18,
recentRatedDays: 30,
recentDays: 30,
warmingUp: true,
series: [],
};
@@ -36,6 +38,13 @@ describe("<TrainingLoadCard>", () => {
expect(html).toMatch(/training-load-fatigue[^]*?>62</);
expect(html).toContain("-8");
expect(html).toContain("Based on 30 days so far");
expect(html).not.toContain('data-slot="training-load-coverage"');
});
it("says how many recent days were rated when some were not", () => {
const html = render({ ...OK, recentRatedDays: 25, recentDays: 42 });
expect(html).toContain('data-slot="training-load-coverage"');
expect(html).toContain("25 of the last 42 days have heart-rate data");
});
it("signs a positive form and drops the warm-up note once covered", () => {
@@ -47,6 +47,17 @@ export function TrainingLoadCard({
))}
</dl>
<p className="text-sm">{t("insights.workouts.load.explainer")}</p>
{trainingLoad.recentRatedDays < trainingLoad.recentDays ? (
<p
className="text-muted-foreground text-xs"
data-slot="training-load-coverage"
>
{t("insights.workouts.load.coverage", {
rated: trainingLoad.recentRatedDays,
days: trainingLoad.recentDays,
})}
</p>
) : null}
{trainingLoad.warmingUp ? (
<p className="text-muted-foreground text-xs">
{t("insights.workouts.load.warmingUp", {