fix(dashboard): paint the blood-glucose tile for untagged readings

Tile eligibility filtered the four named contexts against the per-context
summaries and asked whether any of them held readings. An account whose
source records no meal time matched none of them, so the tile was omitted
with the module on, the layout toggle on, and a reading from today. Nothing
warned; the strip was simply short one card.

Move the gate out of the page into `resolveGlucoseTiles`, which walks the
shared bucket list — untagged included — and hand the caller the label key
with the summary. Both summary producers group through the shared resolver,
so `UNSPECIFIED` reaches the wire with its own count, latest value and
trend, and a tagged account still gets its per-context breakdown.

Refs #943
This commit is contained in:
Marc-André Bombeck committed 2026-09-09 23:03:21 +02:00
1 parent 9a2b1f0955
commit 5ab7a2ca81
8 files changed
+162 -51

No files matched your search

+10 -9
View File
@@ -34,6 +34,7 @@ import {
GLUCOSE_PANEL_WINDOW_DAYS,
type GlucoseClinicalMetrics,
} from "@/lib/analytics/glucose-metrics";
import { groupByGlucoseContext } from "@/lib/glucose";
import {
decisionForEvidence,
PATTERN_FAMILIES,
@@ -495,16 +496,16 @@ async function buildAnalyticsResponse(user: AuthedUser, locale: Locale) {
orderBy: { measuredAt: "asc" },
select: { value: true, measuredAt: true, glucoseContext: true },
});
// #943 — grouped through the shared bucket resolver, so readings with no
// meal-time tag land in `UNSPECIFIED` instead of being dropped on the floor.
const glucoseByContext: Record<string, ReturnType<typeof summarize>> = {};
if (glucoseRows.length > 0) {
const contexts = ["FASTING", "POSTPRANDIAL", "RANDOM", "BEDTIME"] as const;
for (const ctx of contexts) {
const ctxRows = glucoseRows.filter((r) => r.glucoseContext === ctx);
if (ctxRows.length === 0) continue;
glucoseByContext[ctx] = summarize(
ctxRows.map((r): DataPoint => ({ date: r.measuredAt, value: r.value })),
);
}
for (const [bucket, ctxRows] of groupByGlucoseContext(
glucoseRows,
(r) => r.glucoseContext,
)) {
glucoseByContext[bucket] = summarize(
ctxRows.map((r): DataPoint => ({ date: r.measuredAt, value: r.value })),
);
}
// v1.17.0 — server-authoritative glucose clinical panel. The TIR / GMI /