mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(targets): show the weight target the user set, not the WHO band
The targets reference panel graded weight against the WHO BMI band even for an account that had entered its own target, so the panel under the weight chart contradicted the chart above it. An override now wins and the card's source reads Custom, the same posture the glucose builder has always taken with a user-pinned band. The BMI classification stays WHO-derived: naming which category a reading falls in is a reference fact, not a goal.
This commit is contained in:
1 parent
be348f8820
commit
a7fcd6d249
3 files changed
+59
-3
No files matched your search
@@ -20,6 +20,7 @@ describe("target section builders", () => {
|
||||
gender: null,
|
||||
timezone: TZ,
|
||||
now: NOW,
|
||||
weightTargetOverride: null,
|
||||
});
|
||||
|
||||
expect(result.targets.map((target) => target.type)).toEqual([
|
||||
@@ -163,3 +164,39 @@ describe("target section builders", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* v1.34 — the weight card grades against the user's own target when one is set,
|
||||
* so the `/targets` reference panel and the weight chart above it name the same
|
||||
* band. The panel used to show the WHO BMI band unconditionally.
|
||||
*/
|
||||
describe("buildVitalTargets — user weight target", () => {
|
||||
const base = {
|
||||
recentMeasurements: [],
|
||||
latestByType: {},
|
||||
average30ByType: {},
|
||||
heightCm: 178,
|
||||
age: 40,
|
||||
gender: null,
|
||||
timezone: TZ,
|
||||
now: NOW,
|
||||
};
|
||||
|
||||
it("shows the WHO BMI band when no target is set", () => {
|
||||
const weight = buildVitalTargets({
|
||||
...base,
|
||||
weightTargetOverride: null,
|
||||
}).targets.find((t) => t.type === "WEIGHT");
|
||||
expect(weight?.source).toBe("WHO BMI");
|
||||
expect(weight?.range).not.toEqual({ min: 74, max: 78 });
|
||||
});
|
||||
|
||||
it("shows the user's own band, labelled as theirs, when one is set", () => {
|
||||
const weight = buildVitalTargets({
|
||||
...base,
|
||||
weightTargetOverride: { min: 74, max: 78 },
|
||||
}).targets.find((t) => t.type === "WEIGHT");
|
||||
expect(weight?.range).toEqual({ min: 74, max: 78 });
|
||||
expect(weight?.source).toBe("Custom");
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,10 @@ import { SCHEDULE_COMPLIANCE_SELECT } from "@/lib/analytics/compliance";
|
||||
import { DEFAULT_TIMEZONE } from "@/lib/tz/resolver";
|
||||
import pLimit from "p-limit";
|
||||
import type { MeasurementType } from "@/generated/prisma/client";
|
||||
import type { ThresholdOverridesJson } from "@/lib/analytics/effective-range";
|
||||
import {
|
||||
resolveWeightTargetOverride,
|
||||
type ThresholdOverridesJson,
|
||||
} from "@/lib/analytics/effective-range";
|
||||
import { resolveGlucoseUnit } from "@/lib/glucose";
|
||||
import { toProfileSex } from "@/lib/profile/sex";
|
||||
import {
|
||||
@@ -264,6 +267,10 @@ export async function buildTargetsResponse(user: AuthedUser) {
|
||||
gender,
|
||||
timezone,
|
||||
now,
|
||||
// v1.34 — the weight card grades against the user's OWN target when they
|
||||
// set one, so the reference panel and the weight chart above it name the
|
||||
// same band.
|
||||
weightTargetOverride: resolveWeightTargetOverride(dbUser?.thresholdsJson),
|
||||
});
|
||||
const sleepTarget = buildSleepTarget({
|
||||
sleepStageRows,
|
||||
|
||||
@@ -47,6 +47,13 @@ interface VitalTargetsInput {
|
||||
gender: ProfileSex;
|
||||
timezone: string;
|
||||
now: Date;
|
||||
/**
|
||||
* v1.34 — the user's own weight band from `User.thresholdsJson`, in kg.
|
||||
* When present it replaces the WHO BMI band on the weight card and the
|
||||
* card's source reads "Custom", matching how the glucose builder has
|
||||
* always treated a user override. `null` keeps the height-derived band.
|
||||
*/
|
||||
weightTargetOverride: { min: number; max: number } | null;
|
||||
}
|
||||
|
||||
export interface VitalTargetsResult {
|
||||
@@ -140,6 +147,7 @@ export function buildVitalTargets({
|
||||
gender,
|
||||
timezone,
|
||||
now,
|
||||
weightTargetOverride,
|
||||
}: VitalTargetsInput): VitalTargetsResult {
|
||||
const targets: TargetItem[] = [];
|
||||
const consistencyClock = { timezone, now };
|
||||
@@ -148,7 +156,11 @@ export function buildVitalTargets({
|
||||
// answers a null with a neutral value, never with one of the two sides.
|
||||
const referenceSex = binaryReferenceSex(gender);
|
||||
|
||||
const weightRange = heightCm ? getWeightRange(heightCm) : null;
|
||||
// v1.34 — a user-set target outranks the WHO BMI band. The BMI
|
||||
// classification below stays WHO-derived either way: it names which BMI
|
||||
// category a reading falls in, which is a reference fact and not a goal.
|
||||
const weightRange =
|
||||
weightTargetOverride ?? (heightCm ? getWeightRange(heightCm) : null);
|
||||
let weightClassification: TargetItem["classification"] = null;
|
||||
if (latestByType.WEIGHT != null && heightCm) {
|
||||
const heightM = heightCm / 100;
|
||||
@@ -169,7 +181,7 @@ export function buildVitalTargets({
|
||||
unit: "kg",
|
||||
range: weightRange,
|
||||
classification: weightClassification,
|
||||
source: "WHO BMI",
|
||||
source: weightTargetOverride ? "Custom" : "WHO BMI",
|
||||
...rollupConsistency({
|
||||
events: recentMeasurements.filter(
|
||||
(measurement) => measurement.type === "WEIGHT",
|
||||
|
||||
Reference in new issue
Block a user