From dae7138fc6f534183734aba8f404433b0807dd59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 19 Jun 2026 00:03:22 +0200 Subject: [PATCH] fix(settings): keep the health-record entry reachable regardless of the doctorReport opt-out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The health-record section (PDF + FHIR R4 export) was the only entry-point gated on the doctorReport module's nav toggle, so flipping that opt-out in the Modules hub made the whole section vanish from Settings with no in-context way back. Drop the nav gate: the entry-point now always shows. The doctorReport module still governs the data layer — the server-side /api/export/health-record route stays the hard enforcement (403 module.disabled over Bearer when opted out). Assert the entry shows for a default, empty, and opted-out modules map. --- .../settings/__tests__/settings-shell.test.tsx | 16 +++++++++++----- .../settings/gesundheitsakte-section.tsx | 10 ++++++---- src/components/settings/settings-shell.tsx | 16 +++++++++++----- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/components/settings/__tests__/settings-shell.test.tsx b/src/components/settings/__tests__/settings-shell.test.tsx index 275a4ec9f..24caa863b 100644 --- a/src/components/settings/__tests__/settings-shell.test.tsx +++ b/src/components/settings/__tests__/settings-shell.test.tsx @@ -239,17 +239,23 @@ describe("", () => { expect(disabled).not.toContain('href="/settings/coach"'); }); - it("module-gates the Health-record entry off `user.modules.doctorReport` (B3)", () => { - // Fail-open default: doctorReport undefined → entry shown. + it("always surfaces the Health-record entry, regardless of `doctorReport` (v1.18.6.1)", () => { + // The health-record (PDF / FHIR) entry is a flagship export capability + // and is no longer nav-gated: a default/unset modules map shows it, and + // even an explicit `doctorReport: false` keeps the Settings entry-point + // reachable. The server-side `/api/export/health-record` route remains + // the hard enforcement of the opt-out. expect(renderShell({ active: "account" })).toContain( 'href="/settings/gesundheitsakte"', ); - // Explicitly disabled → entry hidden. - const disabled = renderShell({ + expect(renderShell({ active: "account", modules: {} })).toContain( + 'href="/settings/gesundheitsakte"', + ); + const optedOut = renderShell({ active: "account", modules: { doctorReport: false }, }); - expect(disabled).not.toContain('href="/settings/gesundheitsakte"'); + expect(optedOut).toContain('href="/settings/gesundheitsakte"'); }); it("falls back to `account` when the pathname doesn't match a known slug", () => { diff --git a/src/components/settings/gesundheitsakte-section.tsx b/src/components/settings/gesundheitsakte-section.tsx index 72ee9adac..df924a4b5 100644 --- a/src/components/settings/gesundheitsakte-section.tsx +++ b/src/components/settings/gesundheitsakte-section.tsx @@ -10,10 +10,12 @@ * the generic CSV / JSON data-out paths. Export & Import now keeps only * the generic data export/import surfaces. * - * Module-gated on `doctorReport` (v1.18.0 B3): the nav entry hides when the - * module is off (see `SETTINGS_SECTIONS` in `settings-shell.tsx`), and the - * server-side `/api/export/health-record` route refuses with a 403 - * `module.disabled` envelope — so a disabled doctor-report surface cannot be + * The `doctorReport` module governs the data layer, not the Settings + * entry-point: as of v1.18.6.1 the nav entry always shows (the health record + * is a flagship export capability and should never silently vanish from + * Settings). The server-side `/api/export/health-record` route is the hard + * enforcement — it refuses with a 403 `module.disabled` envelope when the + * account has opted out, so a disabled doctor-report surface cannot be * reached over a Bearer token either. */ diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index 5b4ee1a18..6575b2834 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -177,15 +177,21 @@ export const SETTINGS_SECTIONS: readonly SettingsSection[] = [ }, { slug: "api", titleKey: "settings.sections.api.title", icon: KeyRound }, // v1.18.0 (S5) — the full health record (PDF + FHIR R4 + zip package) - // earns its own home, lifted out of Export & Import. Gated on the - // `doctorReport` module: when the user turns the doctor-report surface off, - // the entry hides (the server-side `/api/export/health-record` gate is the - // hard enforcement; this hides the entry-point to match). + // earns its own home, lifted out of Export & Import. + // + // v1.18.6.1 — the nav entry is NOT module-gated. The health record is a + // flagship export capability; gating its only entry-point on the + // `doctorReport` opt-out meant a stray toggle in the Modules hub made the + // whole section vanish with no in-context way back. The entry now always + // shows. The `doctorReport` module still governs the data layer: the + // server-side `/api/export/health-record` route remains the hard + // enforcement (403 `module.disabled` over Bearer when the account opts + // out), and the Modules hub keeps the toggle for anyone who wants the + // surface gone elsewhere — but the Settings entry-point stays reachable. { slug: "gesundheitsakte", titleKey: "settings.sections.gesundheitsakte.title", icon: FileHeart, - moduleGate: "doctorReport", }, { slug: "export",