From eed19fb4abc6f1c68fa4680acf9436f8fab06869 Mon Sep 17 00:00:00 2001 From: Achintya Rajan Date: Mon, 6 Oct 2025 19:36:40 -0700 Subject: [PATCH] slight improvement for Sidebar2 --- .../app/(dashboard)/components/Sidebar2.tsx | 60 ++++++++++++------- ui/litellm-dashboard/src/app/page.tsx | 1 - 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx b/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx index 209cf2e67fd..37901b5e639 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx @@ -1,8 +1,7 @@ "use client"; import React from "react"; -import Link from "next/link"; -import { usePathname, useSearchParams } from "next/navigation"; +import { usePathname, useSearchParams, useRouter } from "next/navigation"; import { Layout, Menu, ConfigProvider } from "antd"; import { KeyOutlined, @@ -62,6 +61,11 @@ const withBase = (relativePath: string) => { }; const Sidebar2: React.FC = ({ accessToken, userRole, defaultSelectedKey, collapsed = false }) => { + const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + + /** ---------- Menu model ---------- */ const menuItems: MenuItem[] = [ { key: "1", page: "api-keys", label: "Virtual Keys", icon: }, { @@ -217,9 +221,7 @@ const Sidebar2: React.FC = ({ accessToken, userRole, defaultSelect children: item.children?.filter((child) => !child.roles || child.roles.includes(userRole)), })); - // Highlight selection based on pathname or ?page= - const pathname = usePathname(); - const searchParams = useSearchParams(); + /** ---------- Selection state ---------- */ const pageParam = searchParams.get("page") || undefined; const findMenuItemKey = (page: string): string => { @@ -244,31 +246,47 @@ const Sidebar2: React.FC = ({ accessToken, userRole, defaultSelect ? findMenuItemKey(defaultSelectedKey) : "1"; - // Root-only routing helper: always replace everything after the domain, honoring base path - const rootWithPage = (p: string) => ({ - pathname: getBasePath() || "/", - query: { page: p }, - }); + /** ---------- Navigation helpers (SPA only) ---------- */ + // Build a root URL ("/" or "/base/") with an updated ?page=... + const goTo = (p: string) => { + const base = getBasePath() || "/"; + const root = base.endsWith("/") ? base : `${base}/`; + const sp = new URLSearchParams(typeof window !== "undefined" ? window.location.search : ""); + sp.set("page", p); + // Use Next router for client navigation on the SAME route (no hard fetch) + router.replace(`${root}?${sp.toString()}`, { scroll: false }); + }; - // Convert to AntD Menu items: - // - "Virtual Keys" routes to "//virtual-keys" - // - All other items (and children) route to "//?page=" + // Keep the /virtual-keys path the same, but avoid route fetches/hard reloads. + const goToVirtualKeys = () => { + const base = getBasePath() || "/"; + const root = base.endsWith("/") ? base : `${base}/`; + const sp = new URLSearchParams(typeof window !== "undefined" ? window.location.search : ""); + sp.set("page", "api-keys"); + + // 1) Client transition to the root with ?page=api-keys so the view updates. + router.replace(`${root}?${sp.toString()}`, { scroll: false }); + + // 2) Cosmetic URL swap to ".../virtual-keys" without navigation (keeps path the same). + const vk = withBase("virtual-keys"); + if (typeof window !== "undefined") { + window.history.replaceState(null, "", vk); + } + }; + + /** ---------- AntD items with onClick handlers ---------- */ const antdItems = filteredMenuItems.map((item) => { const isVirtualKeys = item.key === "1"; - const label = isVirtualKeys ? ( - Virtual Keys - ) : ( - {item.label} - ); - return { key: item.key, icon: item.icon, - label, + label: item.label, // plain text; click handled via onClick + onClick: !item.children ? (isVirtualKeys ? goToVirtualKeys : () => goTo(item.page)) : undefined, children: item.children?.map((child) => ({ key: child.key, icon: child.icon, - label: {child.label}, + label: child.label, + onClick: () => goTo(child.page), })), }; }); diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 8e25dc192c8..104ef41810a 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -39,7 +39,6 @@ import VectorStoreManagement from "@/components/vector_store_management"; import UIThemeSettings from "@/components/ui_theme_settings"; import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; import { cx } from "@/lib/cva.config"; -import useFeatureFlags, { FeatureFlagsProvider } from "@/hooks/useFeatureFlags"; import Sidebar2 from "@/app/(dashboard)/components/Sidebar2"; import SidebarProvider from "@/app/(dashboard)/components/SidebarProvider";