diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index a6bb6245eb..c2479aa175 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -136,6 +136,11 @@ export default function CreateKeyPage() { } const [accessToken, setAccessToken] = useState(null) + const [sidebarCollapsed, setSidebarCollapsed] = useState(false); + + const toggleSidebar = () => { + setSidebarCollapsed(!sidebarCollapsed); + }; const addKey = (data: any) => { setKeys((prevData) => (prevData ? [...prevData, data] : [data])) @@ -248,14 +253,17 @@ export default function CreateKeyPage() { proxySettings={proxySettings} accessToken={accessToken} isPublicPage={false} + sidebarCollapsed={sidebarCollapsed} + onToggleSidebar={toggleSidebar} />
-
+
diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx index 4715b3b642..43c7a21063 100644 --- a/ui/litellm-dashboard/src/components/leftnav.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.tsx @@ -30,6 +30,7 @@ import { } from '@ant-design/icons'; import { old_admin_roles, v2_admin_role_names, all_admin_roles, rolesAllowedToSeeUsage, rolesWithWriteAccess, internalUserRoles, isAdminRole } from '../utils/roles'; import UsageIndicator from './usage_indicator'; +import { ConfigProvider } from 'antd'; const { Sider } = Layout; // Define the props type @@ -38,6 +39,7 @@ interface SidebarProps { setPage: (page: string) => void; userRole: string; defaultSelectedKey: string; + collapsed?: boolean; } // Create a more comprehensive menu item configuration @@ -57,65 +59,73 @@ const Sidebar: React.FC = ({ setPage, userRole, defaultSelectedKey, + collapsed = false, }) => { - const [collapsed, setCollapsed] = useState(false); - const toggleCollapse = () => { - setCollapsed(!collapsed); - }; // Note: If a menu item does not have a role, it is visible to all roles. const menuItems: MenuItem[] = [ - { key: "1", page: "api-keys", label: "Virtual Keys", icon: }, - { key: "3", page: "llm-playground", label: "Test Key", icon: , roles: rolesWithWriteAccess }, - { key: "2", page: "models", label: "Models + Endpoints", icon: , roles: rolesWithWriteAccess }, - { key: "12", page: "new_usage", label: "Usage", icon: , roles: [...all_admin_roles, ...internalUserRoles] }, - { key: "6", page: "teams", label: "Teams", icon: }, - { key: "17", page: "organizations", label: "Organizations", icon: , roles: all_admin_roles }, - { key: "5", page: "users", label: "Internal Users", icon: , roles: all_admin_roles }, - { key: "14", page: "api_ref", label: "API Reference", icon: }, + { + key: "1", + page: "api-keys", + label: "Virtual Keys", + icon: + }, + { + key: "3", + page: "llm-playground", + label: "Test Key", + icon: , + roles: rolesWithWriteAccess + }, + { key: "2", page: "models", label: "Models + Endpoints", icon: , roles: rolesWithWriteAccess }, + { key: "12", page: "new_usage", label: "Usage", icon: , roles: [...all_admin_roles, ...internalUserRoles] }, + { key: "6", page: "teams", label: "Teams", icon: }, + { key: "17", page: "organizations", label: "Organizations", icon: , roles: all_admin_roles }, + { key: "5", page: "users", label: "Internal Users", icon: , roles: all_admin_roles }, + { key: "14", page: "api_ref", label: "API Reference", icon: }, { key: "16", page: "model-hub-table", label: "Model Hub", - icon: + icon: }, - { key: "15", page: "logs", label: "Logs", icon: }, - { key: "11", page: "guardrails", label: "Guardrails", icon: , roles: all_admin_roles }, + { key: "15", page: "logs", label: "Logs", icon: }, + { key: "11", page: "guardrails", label: "Guardrails", icon: , roles: all_admin_roles }, { key: "26", page: "tools", label: "Tools", - icon: , + icon: , children: [ - { key: "18", page: "mcp-servers", label: "MCP Servers", icon: }, - { key: "21", page: "vector-stores", label: "Vector Stores", icon: , roles: all_admin_roles }, + { key: "18", page: "mcp-servers", label: "MCP Servers", icon: }, + { key: "21", page: "vector-stores", label: "Vector Stores", icon: , roles: all_admin_roles }, ] }, { key: "experimental", page: "experimental", label: "Experimental", - icon: , + icon: , children: [ - { key: "9", page: "caching", label: "Caching", icon: , roles: all_admin_roles }, - { key: "25", page: "prompts", label: "Prompts", icon: , roles: all_admin_roles }, - { key: "10", page: "budgets", label: "Budgets", icon: , roles: all_admin_roles }, - { key: "20", page: "transform-request", label: "API Playground", icon: , roles: [...all_admin_roles, ...internalUserRoles] }, - { key: "19", page: "tag-management", label: "Tag Management", icon: , roles: all_admin_roles }, - { key: "4", page: "usage", label: "Old Usage", icon: }, + { key: "9", page: "caching", label: "Caching", icon: , roles: all_admin_roles }, + { key: "25", page: "prompts", label: "Prompts", icon: , roles: all_admin_roles }, + { key: "10", page: "budgets", label: "Budgets", icon: , roles: all_admin_roles }, + { key: "20", page: "transform-request", label: "API Playground", icon: , roles: [...all_admin_roles, ...internalUserRoles] }, + { key: "19", page: "tag-management", label: "Tag Management", icon: , roles: all_admin_roles }, + { key: "4", page: "usage", label: "Old Usage", icon: }, ] }, { key: "settings", page: "settings", label: "Settings", - icon: , + icon: , roles: all_admin_roles, children: [ - { key: "11", page: "general-settings", label: "Router Settings", icon: , roles: all_admin_roles }, - { key: "8", page: "settings", label: "Logging & Alerts", icon: , roles: all_admin_roles }, - { key: "13", page: "admin-panel", label: "Admin Settings", icon: , roles: all_admin_roles }, - { key: "14", page: "ui-theme", label: "UI Theme", icon: , roles: all_admin_roles }, + { key: "11", page: "general-settings", label: "Router Settings", icon: , roles: all_admin_roles }, + { key: "8", page: "settings", label: "Logging & Alerts", icon: , roles: all_admin_roles }, + { key: "13", page: "admin-panel", label: "Admin Settings", icon: , roles: all_admin_roles }, + { key: "14", page: "ui-theme", label: "UI Theme", icon: , roles: all_admin_roles }, ] } ]; @@ -166,83 +176,32 @@ const Sidebar: React.FC = ({ collapsible trigger={null} style={{ - transition: 'all 0.2s', + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', // Material Design easing position: 'relative', }} > - , - label: '', - onClick: toggleCollapse, - style: { - cursor: 'pointer', + - {filteredMenuItems[0]?.label} -
{ - e.stopPropagation(); - toggleCollapse(); - }} - style={{ - cursor: 'pointer', - padding: '8px 12px', - borderRadius: '4px', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - fontSize: '12px', - color: '#888', - transition: 'all 0.2s', - marginLeft: '8px', - marginRight: '-8px', - }} - onMouseEnter={(e) => { - e.currentTarget.style.background = 'rgba(0, 0, 0, 0.04)'; - e.currentTarget.style.color = '#666'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.background = 'transparent'; - e.currentTarget.style.color = '#888'; - }} - title="Collapse navigation sidebar" - > - -
-
- ), - onClick: !filteredMenuItems[0]?.children ? () => { - const newSearchParams = new URLSearchParams(window.location.search); - newSearchParams.set('page', filteredMenuItems[0]?.page || ''); - window.history.pushState(null, '', `?${newSearchParams.toString()}`); - setPage(filteredMenuItems[0]?.page || ''); - } : undefined }, - // Rest of the menu items (or all items when collapsed) - ...(collapsed ? filteredMenuItems : filteredMenuItems.slice(1)).map(item => ({ + }} + > + ({ key: item.key, icon: item.icon, label: item.label, @@ -263,9 +222,9 @@ const Sidebar: React.FC = ({ window.history.pushState(null, '', `?${newSearchParams.toString()}`); setPage(item.page); } : undefined - })) - ]} - /> + }))} + /> + { isAdminRole(userRole) && !collapsed && ( diff --git a/ui/litellm-dashboard/src/components/navbar.tsx b/ui/litellm-dashboard/src/components/navbar.tsx index d6a797cc73..66b9fdc18d 100644 --- a/ui/litellm-dashboard/src/components/navbar.tsx +++ b/ui/litellm-dashboard/src/components/navbar.tsx @@ -11,7 +11,9 @@ import { BgColorsOutlined, CrownOutlined, MailOutlined, - SafetyOutlined + SafetyOutlined, + MenuFoldOutlined, + MenuUnfoldOutlined, } from '@ant-design/icons' import { clearTokenCookies } from "@/utils/cookieUtils" import { fetchProxySettings } from "@/utils/proxyUtils" @@ -23,10 +25,12 @@ interface NavbarProps { userEmail: string | null; userRole: string | null; premiumUser: boolean; - setProxySettings: React.Dispatch>; proxySettings: any; + setProxySettings: React.Dispatch>; accessToken: string | null; isPublicPage: boolean; + sidebarCollapsed?: boolean; + onToggleSidebar?: () => void; } const Navbar: React.FC = ({ @@ -38,6 +42,8 @@ const Navbar: React.FC = ({ setProxySettings, accessToken, isPublicPage = false, + sidebarCollapsed = false, + onToggleSidebar, }) => { const baseUrl = getProxyBaseUrl(); const [logoutUrl, setLogoutUrl] = useState(""); @@ -128,14 +134,27 @@ const Navbar: React.FC = ({ return (