Wire built-in admin screens into the global menu (todo §5); extract adminSection() = one permission-gated 'Admin' header (Users/Groups/Roles), reused by both the home dashboard menu and the in-screen adminNav so they can't drift. composeNav drops the whole gated header+subtree for a non-admin/anonymous (cosmetic — the admin routes stay independently GuardError(403)-gated); narrowed AdminScreen to groups|roles|users. Reuses existing sprite icons (no icon-guard change); default anonymous / render byte-equivalent so visual E2E unaffected. Tests-first: dashboard model gating (admin→3 hrefs, non-admin→absent) + app HTTP (admin JWT→/admin/users link, anon→absent). Stability-reviewer run as a local PR: APPROVE, no Critical/High/Medium. README Layout updated. 242→244 units + typecheck green
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
// parseListQuery → filter/sort/paginate a mock dataset → composeNav. Mock data stands in for
|
||||
// upstream until §4; the filter form, sortable headers and pager all round-trip the URL (zero-JS).
|
||||
|
||||
import { adminSection } from "./admin-nav.ts";
|
||||
import type { User } from "./context.ts";
|
||||
import { DEFAULT_MENU, type MenuConfig } from "./menu-config.ts";
|
||||
import { composeNav, type NavNode, type NavOverride } from "./nav.ts";
|
||||
@@ -128,6 +129,7 @@ function nav(roles: string[], override: NavOverride): NavNode[] {
|
||||
{ href: "#exports", id: "exports", label: "Exports" },
|
||||
], icon: "i-chart", id: "reports", label: "Reports", open: true },
|
||||
{ href: "#settings", icon: "i-gear", id: "settings", label: "Settings", permission: "admin" },
|
||||
adminSection(), // built-in Users/Groups/Roles screens; gated → invisible to non-admins
|
||||
]], override, roles);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user