diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index d65d8e72374..f547ab7d057 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -407,7 +407,7 @@ export default function CreateKeyPage() { /> ) : page == "api_ref" ? ( - ) : page == "settings" ? ( + ) : page == "logging-and-alerts" ? ( ) : page == "budgets" ? ( @@ -419,7 +419,7 @@ export default function CreateKeyPage() { ) : page == "transform-request" ? ( - ) : page == "general-settings" ? ( + ) : page == "router-settings" ? ( ) : page == "ui-theme" ? ( - ) : page == "cost-tracking-settings" ? ( + ) : page == "cost-tracking" ? ( ) : page == "model-hub-table" ? ( isAdminRole(userRole) ? ( diff --git a/ui/litellm-dashboard/src/components/leftnav.test.tsx b/ui/litellm-dashboard/src/components/leftnav.test.tsx index 7f07c9c1b35..1512c8b9350 100644 --- a/ui/litellm-dashboard/src/components/leftnav.test.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.test.tsx @@ -66,4 +66,33 @@ describe("Sidebar (leftnav)", () => { expect(getByText("Search Tools")).toBeInTheDocument(); }); }); + it("has no duplicate keys among all menu items and their children", () => { + // Helper to recursively extract all keys from Ant Design Menu items + function getAllKeysFromMenu(wrapper: HTMLElement): string[] { + const allKeys: string[] = []; + // Ant Design renders key as data-menu-id or inside attributes, but for this case, we look for text as fallback. + // For a generic check, here we fetch ids from rendered list items, and also descend into submenus + const items = wrapper.querySelectorAll("[data-menu-id]"); + items.forEach((item) => { + const dataMenuId = item.getAttribute("data-menu-id"); + if (dataMenuId) { + allKeys.push(dataMenuId); + } + }); + return allKeys; + } + + const { container } = render(); + const allRenderedKeys = getAllKeysFromMenu(container); + + const keySet = new Set(); + const duplicates: string[] = []; + for (const key of allRenderedKeys) { + if (keySet.has(key)) { + duplicates.push(key); + } + keySet.add(key); + } + expect(duplicates).toHaveLength(0); + }); }); diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx index efd9f1bb560..78d8a3940ce 100644 --- a/ui/litellm-dashboard/src/components/leftnav.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.tsx @@ -49,84 +49,89 @@ const Sidebar: React.FC = ({ accessToken, setPage, userRole, defau // Note: If a menu item does not have a role, it is visible to all roles. const menuItems: MenuItem[] = [ { - key: "1", + key: "api-keys", page: "api-keys", label: "Virtual Keys", icon: , }, { - key: "3", + key: "llm-playground", page: "llm-playground", label: "Playground", icon: , roles: rolesWithWriteAccess, }, { - key: "2", + key: "models", page: "models", label: "Models + Endpoints", icon: , roles: rolesWithWriteAccess, }, { - key: "12", + key: "new_usage", page: "new_usage", label: "Usage", icon: , roles: [...all_admin_roles, ...internalUserRoles], }, - { key: "6", page: "teams", label: "Teams", icon: }, + { key: "teams", page: "teams", label: "Teams", icon: }, { - key: "17", + key: "organizations", page: "organizations", label: "Organizations", icon: , roles: all_admin_roles, }, { - key: "5", + key: "users", page: "users", label: "Internal Users", icon: , roles: all_admin_roles, }, { - key: "10", + key: "budgets", page: "budgets", label: "Budgets", icon: , roles: all_admin_roles, }, - { key: "14", page: "api_ref", label: "API Reference", icon: }, + { key: "api_ref", page: "api_ref", label: "API Reference", icon: }, { - key: "16", + key: "model-hub-table", page: "model-hub-table", label: "AI Hub", icon: , }, - { key: "15", page: "logs", label: "Logs", icon: }, + { key: "logs", page: "logs", label: "Logs", icon: }, { - key: "11", + key: "guardrails", page: "guardrails", label: "Guardrails", icon: , roles: all_admin_roles, }, - { key: "18", page: "mcp-servers", label: "MCP Servers", icon: }, { - key: "26", + key: "mcp-servers", + page: "mcp-servers", + label: "MCP Servers", + icon: , + }, + { + key: "tools", page: "tools", label: "Tools", icon: , children: [ { - key: "28", + key: "search-tools", page: "search-tools", label: "Search Tools", icon: , }, { - key: "21", + key: "vector-stores", page: "vector-stores", label: "Vector Stores", icon: , @@ -141,35 +146,35 @@ const Sidebar: React.FC = ({ accessToken, setPage, userRole, defau icon: , children: [ { - key: "9", + key: "caching", page: "caching", label: "Caching", icon: , roles: all_admin_roles, }, { - key: "29", + key: "agents", page: "agents", label: "Agents", icon: , roles: rolesWithWriteAccess, }, { - key: "25", + key: "prompts", page: "prompts", label: "Prompts", icon: , roles: all_admin_roles, }, { - key: "20", + key: "transform-request", page: "transform-request", label: "API Playground", icon: , roles: [...all_admin_roles, ...internalUserRoles], }, { - key: "19", + key: "tag-management", page: "tag-management", label: "Tag Management", icon: , @@ -186,35 +191,35 @@ const Sidebar: React.FC = ({ accessToken, setPage, userRole, defau roles: all_admin_roles, children: [ { - key: "11", - page: "general-settings", + key: "router-settings", + page: "router-settings", label: "Router Settings", icon: , roles: all_admin_roles, }, { - key: "8", - page: "settings", + key: "logging-and-alerts", + page: "logging-and-alerts", label: "Logging & Alerts", icon: , roles: all_admin_roles, }, { - key: "13", + key: "admin-panel", page: "admin-panel", label: "Admin Settings", icon: , roles: all_admin_roles, }, { - key: "27", - page: "cost-tracking-settings", + key: "cost-tracking", + page: "cost-tracking", label: "Cost Tracking", icon: , roles: all_admin_roles, }, { - key: "14", + key: "ui-theme", page: "ui-theme", label: "UI Theme", icon: ,