mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 04:32:42 +00:00
The Personal Health Score weighted the 0.20 mood-stability pillar unconditionally — the fast-path read mood entries regardless of the account's module state, so a user who turned the mood module off was still scored (and silently penalised or rewarded) by a pillar hidden everywhere else in the product. The dashboard already blanks the mood tile and the coach snapshot unions mood out; the score ring and the insights card pillar list did not. Thread a default-on `moodEnabled` flag through the fast-path. When the caller resolves the mood module as disabled the helper skips the mood read entirely and leaves the series empty, so `moodStability` returns null and the existing proportional null-redistribution re-scales the surviving pillars to sum to 100. The analytics route resolves the module server-side and threads the flag; the insights card hides the Mood row via a new SSR-safe `useModuleEnabled` accessor so it never advertises a pillar the account turned off. Golden fixtures pin mood-on vs mood-off to different, correctly re-weighted scores and assert the disabled read is never issued. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
34 lines
1.3 KiB
TypeScript
34 lines
1.3 KiB
TypeScript
"use client";
|
|
|
|
import { useAuth } from "@/hooks/use-auth";
|
|
import { useQueryClientMounted } from "@/hooks/_internal/use-query-client-safe";
|
|
import type { ModuleKey } from "@/lib/modules/registry";
|
|
|
|
/**
|
|
* v1.18.0 R4 — SSR-safe accessor for a per-account module flag.
|
|
*
|
|
* Mirrors `useDisableCoach()`: `useAuth()` calls `useQuery` directly and
|
|
* crashes when a legacy presentational component (`<HeroStrip>`, the
|
|
* isolated score-card snapshots, …) renders without a
|
|
* `<QueryClientProvider>`. We gate the inner hook on
|
|
* `useQueryClientMounted()` and short-circuit to the default-on result
|
|
* when no client is in the tree — the branch is stable across the
|
|
* component's lifetime, so the conditional call is safe per the Rules of
|
|
* Hooks.
|
|
*
|
|
* Default-on (disabled allowlist): an absent map, a missing key, or any
|
|
* non-`false` value reads as enabled — matching the server gate in
|
|
* `src/lib/modules/gate.ts`.
|
|
*/
|
|
export function useModuleEnabled(moduleKey: ModuleKey): boolean {
|
|
const hasClient = useQueryClientMounted();
|
|
if (!hasClient) return true;
|
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
return useModuleEnabledInner(moduleKey);
|
|
}
|
|
|
|
function useModuleEnabledInner(moduleKey: ModuleKey): boolean {
|
|
const { user } = useAuth();
|
|
return user?.modules?.[moduleKey] !== false;
|
|
}
|