mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(insights): make the nutrients pill reachable once the module is on
The nutrients pill required BOTH the opt-in module on AND at least one NutrientIntakeDay row — but the in-context enable CTA lives on /insights/nutrients itself, so a user who had just turned the module on (or wanted to) had no way to reach the page that would let them. Drop the row-count floor for this one metric: the pill now gates on the module toggle alone, matching the deliberate opt-in the user already made. The page itself is unchanged and still degrades from "enable" to "no rows yet" to the three-card spine.
This commit is contained in:
1 parent
d124a3239d
commit
5f253f7d09
4 files changed
+24
-23
No files matched your search
@@ -106,20 +106,15 @@ export function InsightsLayoutShell({ children }: { children: ReactNode }) {
|
||||
// dashboard tile so navigation between surfaces is a free cache hit.
|
||||
const workoutsProbe = useWorkouts({ limit: 1 });
|
||||
|
||||
// v1.29 — nutrients-presence gate. The nutrients pill gates on at
|
||||
// least one `NutrientIntakeDay` row in a 1-day probe window (any
|
||||
// nutrient, any source). Enabled only when the opt-in module is on —
|
||||
// the route 403s "module.disabled" otherwise, so probing while off
|
||||
// would just burn a request every mount.
|
||||
// v1.29 — nutrients gate. `nutrients` is opt-in (default off), unlike
|
||||
// the always-on metric clusters, so the pill's floor is the module
|
||||
// toggle alone — not a data-presence probe. Gating on BOTH the toggle
|
||||
// AND a first row was a catch-22: the in-context enable CTA lives on
|
||||
// `/insights/nutrients` itself, and that page was only reachable once
|
||||
// data already existed. A user who just flipped the module on (or
|
||||
// wants to) can now always reach the page; the page itself still
|
||||
// degrades from "enable" → "no rows yet" → the three-card spine.
|
||||
const nutrientsEnabled = user?.modules?.nutrients === true;
|
||||
const nutrientsProbe = useQuery({
|
||||
queryKey: queryKeys.nutrientIntake(1),
|
||||
queryFn: () =>
|
||||
apiGet<{ nutrients: Array<{ nutrient: string }> }>(
|
||||
"/api/nutrients?days=1",
|
||||
),
|
||||
enabled: isAuthenticated && nutrientsEnabled,
|
||||
});
|
||||
|
||||
// v1.4.31 — memoise the `availability` prop so an unchanged
|
||||
// payload doesn't recreate the object on every cache-write of
|
||||
@@ -132,7 +127,7 @@ export function InsightsLayoutShell({ children }: { children: ReactNode }) {
|
||||
const hasMood = (comprehensiveQuery.data?.moodSummary?.count ?? 0) > 0;
|
||||
const hasMedication = (comprehensiveQuery.data?.medications?.length ?? 0) > 0;
|
||||
const hasWorkouts = (workoutsProbe.data?.workouts.length ?? 0) > 0;
|
||||
const hasNutrients = (nutrientsProbe.data?.nutrients.length ?? 0) > 0;
|
||||
const hasNutrients = nutrientsEnabled;
|
||||
const availability: InsightInputs | undefined = useMemo(() => {
|
||||
if (!isAuthenticated) return undefined;
|
||||
return { summaries, hasMood, hasMedication, hasWorkouts, hasNutrients };
|
||||
|
||||
@@ -366,8 +366,10 @@ const SUB_PAGE_MODULE: Partial<Record<SubPageSlug, ModuleKey>> = {
|
||||
"breathing-disturbances": "sleep",
|
||||
"blood-glucose": "glucose",
|
||||
workouts: "workouts",
|
||||
// v1.29 — the nutrients pill gates on the opt-in `nutrients` module on
|
||||
// top of the `hasNutrients` data floor.
|
||||
// v1.29 — the nutrients pill gates on the opt-in `nutrients` module
|
||||
// alone; there is no data floor on top (see `hasNutrients` in
|
||||
// `insights-layout-shell.tsx` — reachability, not row count, is the
|
||||
// gate once the user has opted in).
|
||||
nutrients: "nutrients",
|
||||
};
|
||||
|
||||
|
||||
@@ -123,7 +123,8 @@ export type InsightMetric =
|
||||
// v1.29 — nutrient intake (hydration + micronutrients). Event/store
|
||||
// driven (`NutrientIntakeDay`, not a `Measurement` series) like
|
||||
// `MOOD` / `MEDICATION` / `WORKOUTS`, so it reads the boolean
|
||||
// `hasNutrients` flag rather than a `summaries[…].count`.
|
||||
// `hasNutrients` flag rather than a `summaries[…].count` — and unlike
|
||||
// those, the flag tracks the opt-in module toggle, not row presence.
|
||||
| "NUTRIENTS";
|
||||
|
||||
/**
|
||||
@@ -147,10 +148,13 @@ export interface InsightInputs {
|
||||
*/
|
||||
hasWorkouts?: boolean;
|
||||
/**
|
||||
* v1.29 — whether the user has at least one `NutrientIntakeDay` row in
|
||||
* the probe window (any nutrient, any source). Drives the nutrients
|
||||
* pill. Optional so legacy mounts keep type-checking; the helper
|
||||
* treats `undefined` as "not available".
|
||||
* v1.29 — whether the opt-in `nutrients` module is on. Drives the
|
||||
* nutrients pill directly (no row-count floor on top): the module is
|
||||
* a deliberate user choice, and the in-context enable CTA lives on
|
||||
* `/insights/nutrients` itself, so the pill must stay reachable the
|
||||
* moment the module is on even with zero rows recorded yet. Optional
|
||||
* so legacy mounts keep type-checking; the helper treats `undefined`
|
||||
* as "not available".
|
||||
*/
|
||||
hasNutrients?: boolean;
|
||||
}
|
||||
|
||||
@@ -138,8 +138,8 @@ const SUB_PAGE_METRIC = {
|
||||
// v1.29 — nutrient intake (hydration + micronutrients). Backed by
|
||||
// `NutrientIntakeDay`, not a `Measurement` series, so the metric list
|
||||
// stays empty like `medications` / `workouts`; the tab-strip pill gates
|
||||
// on `hasNutrients` (threaded from a lightweight probe read) instead of
|
||||
// a `summaries[…].count`.
|
||||
// on `hasNutrients`, which tracks the opt-in module toggle rather than
|
||||
// a `summaries[…].count` or a row-presence probe.
|
||||
nutrients: [],
|
||||
} as const satisfies Record<string, readonly string[]>;
|
||||
|
||||
|
||||
Reference in new issue
Block a user