mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
fix(dashboard): wire 7T and 30T sub-values on the BD-Zielbereich tile
The tile rendered the 30-day headline correctly but the `7T:` and
`30T:` sub-values fell back to "—" even when the user had paired BP
readings in both windows. Root cause was that `/api/analytics` only
returned a single `bpInTargetPct`; the tile received `avg7={null}`
and `avg30={null}` and the TrendCard rendered the dash placeholder.
Add `computeBpInTargetWindows()` next to the existing `computeBpInTargetPct()`
helper. It filters the input series by `measuredAt` against the supplied
clock and runs the same pair-and-count logic over the trailing 7-day and
30-day windows. The route fetches the last 30 days of data once, derives
both shares from the result, and surfaces them as `bpInTargetPct7d` and
`bpInTargetPct30d` on the response. The dashboard tile maps them to
`avg7` and `avg30` so the sub-values now show the actual percentages.
Coverage:
- 6 unit tests for the windowed helper (empty input, mixed window data,
null-7d-with-real-30d, default-clock, Marc's production fixture).
- 2 integration tests against testcontainers seeding 30 days of paired
BP measurements; both windows produce the expected hand-counted shares.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
fc328abf49
commit
23363ca05e
5 files changed
+397
-5
No files matched your search
@@ -4,7 +4,10 @@ import { annotate } from "@/lib/logging/context";
|
||||
import { apiSuccess } from "@/lib/api-response";
|
||||
import { summarize, type DataPoint } from "@/lib/analytics/trends";
|
||||
import { getBpTargets } from "@/lib/analytics/bp-targets";
|
||||
import { computeBpInTargetPct } from "@/lib/analytics/bp-in-target";
|
||||
import {
|
||||
computeBpInTargetPct,
|
||||
computeBpInTargetWindows,
|
||||
} from "@/lib/analytics/bp-in-target";
|
||||
import type { MeasurementType } from "@/generated/prisma/client";
|
||||
import { measurementTypeEnum } from "@/lib/validations/measurement";
|
||||
|
||||
@@ -54,9 +57,12 @@ export const GET = apiHandler(async () => {
|
||||
|
||||
// BP in-target percentage (auto-calculated from date of birth)
|
||||
let bpInTargetPct: number | null = null;
|
||||
let bpInTargetPct7d: number | null = null;
|
||||
let bpInTargetPct30d: number | null = null;
|
||||
const bpTargets = getBpTargets(user.dateOfBirth);
|
||||
if (bpTargets) {
|
||||
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
||||
const now = new Date();
|
||||
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
||||
const [sysData, diaData] = await Promise.all([
|
||||
prisma.measurement.findMany({
|
||||
where: {
|
||||
@@ -83,6 +89,22 @@ export const GET = apiHandler(async () => {
|
||||
// read 0 % even when readings clearly sat inside target.
|
||||
const result = computeBpInTargetPct(sysData, diaData, bpTargets);
|
||||
bpInTargetPct = result?.pct ?? null;
|
||||
|
||||
// v1.4.18 A1 — populate the dashboard tile's 7T / 30T sub-values.
|
||||
// The headline (`bpInTargetPct`, also 30 days) is kept for backward
|
||||
// compatibility with cached client bundles; the tile reads the
|
||||
// explicit window fields below for the sub-row. `last30Days` here
|
||||
// is the same calculation as the headline but kept independent so
|
||||
// a future refactor (e.g. headline → "today's reading in target?")
|
||||
// can move without breaking the sub-values.
|
||||
const windows = computeBpInTargetWindows(
|
||||
sysData,
|
||||
diaData,
|
||||
bpTargets,
|
||||
now,
|
||||
);
|
||||
bpInTargetPct7d = windows.last7Days?.pct ?? null;
|
||||
bpInTargetPct30d = windows.last30Days?.pct ?? null;
|
||||
}
|
||||
|
||||
// Per-context glucose summaries (canonical mg/dL).
|
||||
@@ -107,6 +129,8 @@ export const GET = apiHandler(async () => {
|
||||
summaries: results,
|
||||
bmi,
|
||||
bpInTargetPct,
|
||||
bpInTargetPct7d,
|
||||
bpInTargetPct30d,
|
||||
glucoseByContext,
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user