From c89fed0be4c64b4d881942a0fd240f239e239e96 Mon Sep 17 00:00:00 2001 From: hataa <79907651+hata33@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:37:15 +0800 Subject: [PATCH] fix(frontend): static-website gate and deep-link fallback for user management (#6641 follow-ups) (#6691) * fix(frontend): static-website gate and deep-link fallback for user management (#6641 follow-ups) Close the two leftovers from the #6641 review: - user-settings-page now gates on isStaticWebsiteOnly() like model management (the static demo user is nominally an admin, but there is no Gateway to query), and the users adminOnly copy gains the same 'unavailable in demos' sentence the models copy already carries. - the settings dialog derives the active section from the nav actually offered to the current user, so a ?settings=users deep link (or a stale dialog store) for a non-admin falls back to the first section instead of rendering an orphan panel with no highlighted entry. Tests: static demo loads nothing (mutation-verified red), non-admin deep link falls back to models, admin deep link lands on user management; full unit suite green. * test(frontend): pin the static-on x admin matrix cell at the dialog level Dialog-level coverage for the real demo path (review follow-up on #6691): admin + isStaticWebsiteOnly() + ?settings=users keeps the Users nav entry (the splice keys on the role only) while the page-level gate shows the admin-only note and issues no query. Mutation-verified: adding the mode flag to the nav splice turns this test red and only this test. --- frontend/AGENTS.md | 6 +- .../workspace/settings/settings-dialog.tsx | 28 ++++--- .../workspace/settings/user-settings-page.tsx | 9 +- frontend/src/core/i18n/locales/en-US.ts | 3 +- frontend/src/core/i18n/locales/zh-CN.ts | 2 +- .../settings/settings-dialog.dom.test.tsx | 83 +++++++++++++++++++ .../settings/user-settings-page.dom.test.tsx | 14 ++++ 7 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 frontend/tests/unit/components/workspace/settings/settings-dialog.dom.test.tsx diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index fc4c1c5da8..28214af08e 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -306,7 +306,11 @@ accounts through `core/auth/admin-users.ts` → `/api/v1/admin/users`. The page reuses `userSchema` for rows — both endpoints parse against it, so a malformed payload fails the load instead of rendering a phantom row (`disabled` optional — absent means not disabled) — and PATCHes only -`{disabled}`; role stays a read-only column. The Gateway's rejection detail +`{disabled}`; role stays a read-only column. Like model management, the page +also gates on `isStaticWebsiteOnly()` (the demo user is nominally an admin, +but there is no Gateway to query), and the settings dialog falls back to the +first nav section when a deep link or stale store activates a section the +current user's nav does not offer. The Gateway's rejection detail (last-active-admin 409) renders verbatim. A session that turns out to be disabled maps `account_disabled` (see `AUTH_ERROR_CODES`) to a login redirect with a stated reason (login surface only — sign-up keeps diff --git a/frontend/src/components/workspace/settings/settings-dialog.tsx b/frontend/src/components/workspace/settings/settings-dialog.tsx index 9e1c27268f..49175c9850 100644 --- a/frontend/src/components/workspace/settings/settings-dialog.tsx +++ b/frontend/src/components/workspace/settings/settings-dialog.tsx @@ -184,6 +184,14 @@ export function SettingsDialog(props: SettingsDialogProps) { t.settings.sections.notification, t.settings.sections.about, ]); + // A `?settings=
` deep link (or a stale dialog store) can activate + // a section this user's nav does not offer — e.g. user management for a + // non-admin — so fall back to the first available section instead of + // rendering a panel with no highlighted entry. The base list above is + // non-empty, so the fallback itself always resolves. + const activeId = sections.some((section) => section.id === activeSection) + ? activeSection + : sections[0]!.id; return (