feat(mental-health): gate the PHQ-9/GAD-7 check-in behind an opt-in module

The mental-wellbeing screener surface shipped built but unreachable: no nav
entry, no module key, ungated. Add a `mentalHealth` module key (opt-in,
default OFF — a depression/anxiety self-assessment is at least as sensitive
as mood) to the registry, the module-hub icon map, and the exhaustive module
maps. Give the check-in a nav home at /insights/mental-wellbeing, born-gated
on the module so it appears only once the account opts in, and redirect the
page home on a direct hit when the module is off.

Item answers stay encrypted and off the AI Coach / MCP regardless of the
toggle; this only controls whether the surface is visible and reachable.
This commit is contained in:
Marc-André Bombeck committed 2026-06-28 18:25:27 +02:00
1 parent 15e740bc2b
commit f62ba50ff0
7 files changed
+88 -7

No files matched your search

+36 -1
View File
@@ -1,16 +1,51 @@
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { Loader2 } from "lucide-react";
import { useAuth } from "@/hooks/use-auth";
import { MentalWellbeing } from "@/components/mental-health/mental-wellbeing";
/**
* v1.25 — `/insights/mental-wellbeing`.
* v1.25.0 — `/insights/mental-wellbeing`.
*
* Opt-in PHQ-9 / GAD-7 screener surface, beside mood tracking. The screen owns
* its own questionnaire + result + crisis-resource flow; it deliberately does
* NOT mount the shared Coach-launch assessment card (mental-health item content
* is kept out of the AI Coach by construction).
*
* Born-gated on the resolved `modules.mentalHealth` flag from `GET /api/auth/me`
* (opt-in / default-off — the per-user opt-in AND the operator server-wide
* kill-switch). An unauthenticated visitor is bounced to login; an authenticated
* account without the module opted in is bounced home (the nav entry is already
* hidden for them, so this only catches a direct URL hit). Both assessment
* routes also enforce the gate server-side, so this is a UX redirect, not the
* security boundary.
*/
export default function MentalWellbeingPage() {
const { user, isLoading, isAuthenticated } = useAuth();
const router = useRouter();
const enabled = user?.modules?.mentalHealth === true;
useEffect(() => {
if (isLoading) return;
if (!isAuthenticated) {
router.push("/auth/login");
} else if (!enabled) {
router.push("/");
}
}, [isLoading, isAuthenticated, enabled, router]);
if (isLoading || !isAuthenticated || !enabled) {
return (
<div className="flex h-64 items-center justify-center">
<Loader2 className="text-primary h-8 w-8 animate-spin motion-reduce:animate-none" />
</div>
);
}
return (
<div className="p-4 sm:p-6">
<MentalWellbeing />