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:
@@ -26,6 +26,7 @@ import { useScope } from '../hooks/useScope';
|
|||||||
import {
|
import {
|
||||||
buildAppTreeNodes,
|
buildAppTreeNodes,
|
||||||
buildAgentTreeNodes,
|
buildAgentTreeNodes,
|
||||||
|
buildRouteTreeNodes,
|
||||||
buildAdminTreeNodes,
|
buildAdminTreeNodes,
|
||||||
readCollapsed,
|
readCollapsed,
|
||||||
writeCollapsed,
|
writeCollapsed,
|
||||||
@@ -145,6 +146,7 @@ function makeChevron() {
|
|||||||
|
|
||||||
const SK_APPS = 'sidebar:section:apps';
|
const SK_APPS = 'sidebar:section:apps';
|
||||||
const SK_AGENTS = 'sidebar:section:agents';
|
const SK_AGENTS = 'sidebar:section:agents';
|
||||||
|
const SK_ROUTES = 'sidebar:section:routes';
|
||||||
const SK_ADMIN = 'sidebar:section:admin';
|
const SK_ADMIN = 'sidebar:section:admin';
|
||||||
const SK_COLLAPSED = 'sidebar:collapsed';
|
const SK_COLLAPSED = 'sidebar:collapsed';
|
||||||
|
|
||||||
@@ -181,10 +183,11 @@ function LayoutContent() {
|
|||||||
// --- Section open states ------------------------------------------
|
// --- Section open states ------------------------------------------
|
||||||
const [appsOpen, setAppsOpen] = useState(() => readCollapsed(SK_APPS, true));
|
const [appsOpen, setAppsOpen] = useState(() => readCollapsed(SK_APPS, true));
|
||||||
const [agentsOpen, setAgentsOpen] = useState(() => readCollapsed(SK_AGENTS, false));
|
const [agentsOpen, setAgentsOpen] = useState(() => readCollapsed(SK_AGENTS, false));
|
||||||
|
const [routesOpen, setRoutesOpen] = useState(() => readCollapsed(SK_ROUTES, false));
|
||||||
const [adminOpen, setAdminOpen] = useState(() => readCollapsed(SK_ADMIN, false));
|
const [adminOpen, setAdminOpen] = useState(() => readCollapsed(SK_ADMIN, false));
|
||||||
|
|
||||||
// Ref to remember operational section states when switching to admin
|
// 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');
|
const isAdminPage = location.pathname.startsWith('/admin');
|
||||||
|
|
||||||
@@ -193,9 +196,10 @@ function LayoutContent() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isAdminPage && !prevAdminRef.current) {
|
if (isAdminPage && !prevAdminRef.current) {
|
||||||
// Entering admin — save operational states and collapse them
|
// Entering admin — save operational states and collapse them
|
||||||
opsStateRef.current = { apps: appsOpen, agents: agentsOpen };
|
opsStateRef.current = { apps: appsOpen, agents: agentsOpen, routes: routesOpen };
|
||||||
setAppsOpen(false);
|
setAppsOpen(false);
|
||||||
setAgentsOpen(false);
|
setAgentsOpen(false);
|
||||||
|
setRoutesOpen(false);
|
||||||
setAdminOpen(true);
|
setAdminOpen(true);
|
||||||
writeCollapsed(SK_APPS, false);
|
writeCollapsed(SK_APPS, false);
|
||||||
writeCollapsed(SK_AGENTS, false);
|
writeCollapsed(SK_AGENTS, false);
|
||||||
@@ -204,9 +208,11 @@ function LayoutContent() {
|
|||||||
// Leaving admin — restore operational states
|
// Leaving admin — restore operational states
|
||||||
setAppsOpen(opsStateRef.current.apps);
|
setAppsOpen(opsStateRef.current.apps);
|
||||||
setAgentsOpen(opsStateRef.current.agents);
|
setAgentsOpen(opsStateRef.current.agents);
|
||||||
|
setRoutesOpen(opsStateRef.current.routes);
|
||||||
setAdminOpen(false);
|
setAdminOpen(false);
|
||||||
writeCollapsed(SK_APPS, opsStateRef.current.apps);
|
writeCollapsed(SK_APPS, opsStateRef.current.apps);
|
||||||
writeCollapsed(SK_AGENTS, opsStateRef.current.agents);
|
writeCollapsed(SK_AGENTS, opsStateRef.current.agents);
|
||||||
|
writeCollapsed(SK_ROUTES, opsStateRef.current.routes);
|
||||||
writeCollapsed(SK_ADMIN, false);
|
writeCollapsed(SK_ADMIN, false);
|
||||||
}
|
}
|
||||||
prevAdminRef.current = isAdminPage;
|
prevAdminRef.current = isAdminPage;
|
||||||
@@ -235,6 +241,17 @@ function LayoutContent() {
|
|||||||
});
|
});
|
||||||
}, [isAdminPage, navigate]);
|
}, [isAdminPage, navigate]);
|
||||||
|
|
||||||
|
const toggleRoutes = useCallback(() => {
|
||||||
|
if (isAdminPage) {
|
||||||
|
navigate('/exchanges');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setRoutesOpen((prev) => {
|
||||||
|
writeCollapsed(SK_ROUTES, !prev);
|
||||||
|
return !prev;
|
||||||
|
});
|
||||||
|
}, [isAdminPage, navigate]);
|
||||||
|
|
||||||
const toggleAdmin = useCallback(() => {
|
const toggleAdmin = useCallback(() => {
|
||||||
setAdminOpen((prev) => {
|
setAdminOpen((prev) => {
|
||||||
writeCollapsed(SK_ADMIN, !prev);
|
writeCollapsed(SK_ADMIN, !prev);
|
||||||
@@ -282,6 +299,11 @@ function LayoutContent() {
|
|||||||
[sidebarApps],
|
[sidebarApps],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const routeTreeNodes: SidebarTreeNode[] = useMemo(
|
||||||
|
() => buildRouteTreeNodes(sidebarApps, makeStatusDot, makeChevron),
|
||||||
|
[sidebarApps],
|
||||||
|
);
|
||||||
|
|
||||||
const adminTreeNodes: SidebarTreeNode[] = useMemo(
|
const adminTreeNodes: SidebarTreeNode[] = useMemo(
|
||||||
() => buildAdminTreeNodes(),
|
() => buildAdminTreeNodes(),
|
||||||
[],
|
[],
|
||||||
@@ -511,6 +533,24 @@ function LayoutContent() {
|
|||||||
/>
|
/>
|
||||||
</Sidebar.Section>
|
</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 */}
|
{/* When NOT on admin pages, show Admin section at bottom */}
|
||||||
{!isAdminPage && (
|
{!isAdminPage && (
|
||||||
<Sidebar.Section
|
<Sidebar.Section
|
||||||
@@ -564,7 +604,7 @@ function LayoutContent() {
|
|||||||
<ContentTabs active={scope.tab} onChange={setTab} scope={scope} />
|
<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 />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
</AppShell>
|
</AppShell>
|
||||||
|
|||||||
Reference in New Issue
Block a user