fix(settings): make delegated module rows read-only managed deep-links

The Module hub rendered live switches for the delegated keys cycle and coach,
but the gate resolves those from their own sources (cycle from CycleProfile,
coach from the operator flag + disableCoach), so a toggle wrote an inert entry
to modulePreferencesJson, snapped back, and still fired a success toast. Render
delegated modules as read-only rows that deep-link to their real control, strip
delegated keys from the modules PATCH schema so no inert value is stored, drop
the duplicate cycle control, and surface an operator-disabled state with the
previously orphaned hint string.
This commit is contained in:
Marc-André Bombeck committed 2026-06-16 00:51:16 +02:00
1 parent 7c8d82f565
commit 91d01d154a
14 files changed
+216 -36

No files matched your search

+8 -5
View File
@@ -11,15 +11,18 @@
* returns the freshly-resolved module map.
*
* The body schema (`modulePrefsPatchSchema`) is `strict()` over the
* canonical toggleable key set, so a core-domain key (`weight`,
* `bloodPressure`, `pulse`, `medications`) or any unknown key is a 422 —
* the core measurement engine + meds can never be disabled here.
* directly-owned toggleable key set, so a core-domain key (`weight`,
* `bloodPressure`, `pulse`, `medications`), a delegated key (`cycle`,
* `coach`), or any unknown key is a 422 — the core measurement engine +
* meds can never be disabled here, and a value for a delegated module can
* never land inert in `modulePreferencesJson`. The delegated modules are
* managed at their real control (cycle in Account, coach in Settings →
* Coach); the Modules hub deep-links there rather than offering a dead
* toggle.
*
* `userId` is always narrowed from `requireAuth()`; the body never
* carries it. The semantics are a DISABLED allowlist: a key set to
* `false` disables that module; `true` (or absence) leaves it enabled.
* cycle/coach keys are accepted for forward-compat but the gate ignores
* the blob for them (they delegate to their existing source of truth).
*/
import { apiHandler, requireAuth, HttpError } from "@/lib/api-handler";
import {