§10 review pass: address the architecture + product reviewers (todo §10); hide the gated Dashboard nav node from anonymous visitors in buildPluginChrome (a no-permission link to /dashboard only dead-ended them at /login) and dedup it into a shared DASHBOARD_NAV (admin-nav.ts, reused by chrome + adminNav). New chrome.signInHref bakes the current page in as return_to for the shell's anonymous Sign-in link (shell.ejs + reference overview.ejs), mirrored as optional ShellModel.signInHref so the typed builder is complete. ctx.chrome is now a lazy, memoized getter (context.ts chrome option = a factory) so a json/redirect handler or the public "/" with a standalone home never composes the global menu — app.ts passes the app-level memoized factory at every site. Default /dashboard prints a "Starter dashboard" note framing the mock-data home as a replaceable demo (signals its inert affordances); stale "until §4" comments fixed. RESERVED_PLUGIN_IDS drift-guard test derives the built-in segments from AUTH_FLOWS + ADMIN_*_BASE + host literals (home stays deliberately unreserved). Refreshed the stale plugin-contract status blurb and documented the chrome.*→partials/shell mapping. Reviewers: architecture + product APPROVE (no addressable findings remain), stability APPROVE (no Critical/High/Medium). typecheck + 356 units + visual(10) + full-flow(7) E2E green.
This commit is contained in:
+6
-4
@@ -20,6 +20,11 @@ export const ADMIN_CLIENTS_BASE = "/admin/clients";
|
||||
|
||||
export type AdminScreen = "clients" | "groups" | "roles" | "users";
|
||||
|
||||
// The "Dashboard" link to the gated app home (/dashboard). One definition, reused by the in-screen
|
||||
// admin sidebar and the plugin-page chrome (chrome.ts) so the two can't drift. It targets a gated
|
||||
// route, so the chrome hides it from anonymous visitors (a non-signed-in click only dead-ends at /login).
|
||||
export const DASHBOARD_NAV: NavNode = { href: "/dashboard", icon: "i-grid", id: "dashboard", label: "Dashboard" };
|
||||
|
||||
const ITEMS: { href: string; icon: string; id: AdminScreen; label: string }[] = [
|
||||
{ href: ADMIN_USERS_BASE, icon: "i-users", id: "users", label: "Users" },
|
||||
{ href: ADMIN_GROUPS_BASE, icon: "i-layers", id: "groups", label: "Groups" },
|
||||
@@ -42,10 +47,7 @@ export function adminSection(current?: AdminScreen): NavNode {
|
||||
|
||||
// In-screen sidebar for the admin screens: a link home + the admin section (active item marked).
|
||||
export function adminNav(roles: string[], menu: MenuConfig, current: AdminScreen): NavNode[] {
|
||||
return composeNav([[
|
||||
{ href: "/dashboard", icon: "i-grid", id: "dashboard", label: "Dashboard" },
|
||||
adminSection(current),
|
||||
]], menu.override, roles);
|
||||
return composeNav([[DASHBOARD_NAV, adminSection(current)]], menu.override, roles);
|
||||
}
|
||||
|
||||
// The shared gate for every admin screen: a signed-in admin only. Throws GuardError that app.ts maps
|
||||
|
||||
Reference in New Issue
Block a user