fix(#112): add missing Routes section, fix admin double padding

Review feedback: buildRouteTreeNodes was defined but never rendered.
Added Routes section between Agents and Admin. Removed duplicate
padding on admin pages (AdminLayout handles its own padding).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-02 18:32:26 +02:00
parent 00042b1d14
commit d5f5601554

View File

@@ -26,6 +26,7 @@ import { useScope } from '../hooks/useScope';
import {
buildAppTreeNodes,
buildAgentTreeNodes,
buildRouteTreeNodes,
buildAdminTreeNodes,
readCollapsed,
writeCollapsed,
@@ -145,6 +146,7 @@ function makeChevron() {
const SK_APPS = 'sidebar:section:apps';
const SK_AGENTS = 'sidebar:section:agents';
const SK_ROUTES = 'sidebar:section:routes';
const SK_ADMIN = 'sidebar:section:admin';
const SK_COLLAPSED = 'sidebar:collapsed';
@@ -181,10 +183,11 @@ function LayoutContent() {
// --- Section open states ------------------------------------------
const [appsOpen, setAppsOpen] = useState(() => readCollapsed(SK_APPS, true));
const [agentsOpen, setAgentsOpen] = useState(() => readCollapsed(SK_AGENTS, false));
const [routesOpen, setRoutesOpen] = useState(() => readCollapsed(SK_ROUTES, false));
const [adminOpen, setAdminOpen] = useState(() => readCollapsed(SK_ADMIN, false));
// Ref to remember operational section states when switching to admin
const opsStateRef = useRef({ apps: appsOpen, agents: agentsOpen });
const opsStateRef = useRef({ apps: appsOpen, agents: agentsOpen, routes: routesOpen });
const isAdminPage = location.pathname.startsWith('/admin');
@@ -193,9 +196,10 @@ function LayoutContent() {
useEffect(() => {
if (isAdminPage && !prevAdminRef.current) {
// Entering admin — save operational states and collapse them
opsStateRef.current = { apps: appsOpen, agents: agentsOpen };
opsStateRef.current = { apps: appsOpen, agents: agentsOpen, routes: routesOpen };
setAppsOpen(false);
setAgentsOpen(false);
setRoutesOpen(false);
setAdminOpen(true);
writeCollapsed(SK_APPS, false);
writeCollapsed(SK_AGENTS, false);
@@ -204,9 +208,11 @@ function LayoutContent() {
// Leaving admin — restore operational states
setAppsOpen(opsStateRef.current.apps);
setAgentsOpen(opsStateRef.current.agents);
setRoutesOpen(opsStateRef.current.routes);
setAdminOpen(false);
writeCollapsed(SK_APPS, opsStateRef.current.apps);
writeCollapsed(SK_AGENTS, opsStateRef.current.agents);
writeCollapsed(SK_ROUTES, opsStateRef.current.routes);
writeCollapsed(SK_ADMIN, false);
}
prevAdminRef.current = isAdminPage;
@@ -235,6 +241,17 @@ function LayoutContent() {
});
}, [isAdminPage, navigate]);
const toggleRoutes = useCallback(() => {
if (isAdminPage) {
navigate('/exchanges');
return;
}
setRoutesOpen((prev) => {
writeCollapsed(SK_ROUTES, !prev);
return !prev;
});
}, [isAdminPage, navigate]);
const toggleAdmin = useCallback(() => {
setAdminOpen((prev) => {
writeCollapsed(SK_ADMIN, !prev);
@@ -282,6 +299,11 @@ function LayoutContent() {
[sidebarApps],
);
const routeTreeNodes: SidebarTreeNode[] = useMemo(
() => buildRouteTreeNodes(sidebarApps, makeStatusDot, makeChevron),
[sidebarApps],
);
const adminTreeNodes: SidebarTreeNode[] = useMemo(
() => buildAdminTreeNodes(),
[],
@@ -511,6 +533,24 @@ function LayoutContent() {
/>
</Sidebar.Section>
<Sidebar.Section
icon={createElement(GitBranch, { size: 16 })}
label="Routes"
open={routesOpen}
onToggle={toggleRoutes}
>
<SidebarTree
nodes={routeTreeNodes}
selectedPath={sidebarRevealPath ?? location.pathname}
isStarred={isStarred}
onToggleStar={toggleStar}
filterQuery={filterQuery}
persistKey="routes"
autoRevealPath={sidebarRevealPath}
onNavigate={handleSidebarNavigate}
/>
</Sidebar.Section>
{/* When NOT on admin pages, show Admin section at bottom */}
{!isAdminPage && (
<Sidebar.Section
@@ -564,7 +604,7 @@ function LayoutContent() {
<ContentTabs active={scope.tab} onChange={setTab} scope={scope} />
)}
<main style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', minHeight: 0, padding: isAdminPage ? '1.5rem' : 0 }}>
<main style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', minHeight: 0 }}>
<Outlet />
</main>
</AppShell>