From ed0cfda13f183853c7ac1f0b45766793b1301832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 10 May 2026 00:25:00 +0200 Subject: [PATCH] refactor(settings): remove duplicate toggles, route to canonical owner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase B6 commit 3 of 4. The audit (`docs/audit/v1416-settings-audit.md` §2.5 + §2.6) found no live duplicate user-facing toggle between `/settings` and `/admin`: - Notification channels: per-user credentials in `/settings/notifications`, system-wide enable/disable in `/admin` via the global-services API. Different scope, no overlap. - AI providers: per-user override in `/settings/ai`, system default in admin AI settings. Different scope, no overlap. - Channel test buttons appear in both `` and the per-channel cards (`` etc.) but they're different intents (post-failure smoke vs. post-config smoke), kept. - The export buttons (CSV/JSON, doctor-report PDF) used to live in Advanced AND Export — the duplication was already collapsed by phase B7 when the consolidated Export section shipped. What this commit actually does is document the rule in `CLAUDE.md` so future agents don't re-introduce a duplicate. Dead i18n keys (`settings.adminAreaTitle`, `settings.adminAreaDescription`, `settings.openAdminConsole`) are marked stale in the audit doc and will be removed in a follow-up hygiene PR — leaving them in place keeps the i18n locale-parity test green for now. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CLAUDE.md b/CLAUDE.md index 926755eb4..b5822d1fb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -104,6 +104,7 @@ docker compose logs -f app # Tail app logs - **Notifications**: 3 channels (Telegram, ntfy, Web Push). Dispatcher at `src/lib/notifications/dispatcher.ts`. Opt-out model (all events enabled by default) - **Gravatar**: Server-side SHA-256 hash via `src/lib/gravatar.ts`. URL returned in `/api/auth/me` response as `gravatarUrl` field - **Sidebar**: Collapsible (localStorage-persisted). Settings mode with section shortcuts. User section at bottom with three-dot dropdown menu +- **Settings vs Admin scope rule (v1.4.16 phase B6 audit)**: `/settings/` is **per-user** — toggles, credentials, layout, target ranges that affect one account. `/admin/` is **system-wide** — defaults, feature gating, host metrics, audit log read-access. Two surfaces overlap by design: (a) notification channels — admin can disable a channel system-wide via `/api/settings/global-services`, the user sets their own credentials in `/settings/notifications`; (b) AI providers — admin sets the default Codex/OpenAI key, user can override with their own. **No user-facing toggle should appear in both surfaces.** Keys like `settings.adminAreaTitle` are dead code (planned admin-shortcut tile that never shipped) and stay in JSON marked deprecated until a hygiene PR removes them. See `docs/audit/v1416-settings-audit.md`. - **ESLint**: Strict `react-hooks/set-state-in-effect` rule — avoid `setState` inside `useEffect`. Use lazy `useState()` initializer for localStorage reads - **Proxy route protection**: `src/proxy.ts` checks `healthlog_session` cookie; PUBLIC_PATHS array bypasses auth. Security headers (CSP, HSTS, X-Frame-Options) applied to all responses - **PWA safe area**: Root layout has `viewportFit: "cover"`. Bottom-nav uses `pb-[env(safe-area-inset-bottom)]` for iOS safe area