next routing, feature flagged via sidebar

This commit is contained in:
Achintya Rajan 2025-10-08 17:07:46 -07:00
parent 7ed4715a9b
commit 2b0df22dc5
29 changed files with 1012 additions and 399 deletions

View file

@ -0,0 +1,17 @@
"use client";
import APIRef from "@/components/api_ref";
import { useState } from "react";
interface ProxySettings {
PROXY_BASE_URL: string;
PROXY_LOGOUT_URL: string;
}
const APIReferencePage = () => {
const [proxySettings, setProxySettings] = useState<ProxySettings>({ PROXY_BASE_URL: "", PROXY_LOGOUT_URL: "" });
return <APIRef proxySettings={proxySettings} />;
};
export default APIReferencePage;

View file

@ -1,5 +1,6 @@
import { Layout, Menu } from "antd";
import { usePathname } from "next/navigation";
"use client";
import { Layout, Menu, ConfigProvider } from "antd";
import {
KeyOutlined,
PlayCircleOutlined,
@ -18,126 +19,191 @@ import {
ExperimentOutlined,
ToolOutlined,
TagsOutlined,
BgColorsOutlined,
} from "@ant-design/icons";
import { ConfigProvider } from "antd";
import useFeatureFlags from "@/hooks/useFeatureFlags";
// import {
// all_admin_roles,
// rolesWithWriteAccess,
// internalUserRoles,
// isAdminRole,
// } from "../utils/roles";
// import UsageIndicator from "./usage_indicator";
import * as React from "react";
import { useRouter, usePathname } from "next/navigation";
import { all_admin_roles, internalUserRoles, isAdminRole, rolesWithWriteAccess } from "@/utils/roles";
import UsageIndicator from "@/components/usage_indicator";
import React from "react";
const { Sider } = Layout;
// -------- Types --------
interface SidebarProps {
accessToken: string | null;
setPage: (page: string) => void;
userRole: string;
/** Fallback selection id (legacy), used if path can't be matched */
defaultSelectedKey: string;
collapsed?: boolean;
}
interface MenuItem {
interface MenuItemCfg {
key: string;
page: string;
page: string; // legacy id; we map this to a path below
label: string;
roles?: string[];
children?: MenuItem[];
children?: MenuItemCfg[];
icon?: React.ReactNode;
}
/** ---- BASE URL HELPERS ---- */
function normalizeBasePrefix(raw: string | undefined | null): string {
const trimmed = (raw ?? "").trim();
if (!trimmed) return ""; // no base
const core = trimmed.replace(/^\/+/, "").replace(/\/+$/, "");
return core ? `/${core}` : "";
}
const BASE_PREFIX = normalizeBasePrefix(process.env.NEXT_PUBLIC_BASE_URL);
/** ---------- Base URL helpers ---------- */
/**
* Normalizes NEXT_PUBLIC_BASE_URL to either "/" or "/ui/" (always with a trailing slash).
* Supported env values: "" or "ui/".
*/
const getBasePath = () => {
const raw = process.env.NEXT_PUBLIC_BASE_URL ?? "";
const trimmed = raw.replace(/^\/+|\/+$/g, ""); // strip leading/trailing slashes
return trimmed ? `/${trimmed}/` : "/"; // ensure trailing slash
};
/** Build an absolute path under the configured base. */
function withBase(path: string): string {
// path can be "/virtual-keys" or "/?page=..."
const p = path.startsWith("/") ? path : `/${path}`;
return `${BASE_PREFIX}${p}` || p;
}
/** Map legacy `page` ids to real app routes (relative, no leading slash). */
const routeFor = (slug: string): string => {
switch (slug) {
// top level
case "api-keys":
return "virtual-keys";
case "llm-playground":
return "test-key";
case "models":
return "models-and-endpoints";
case "new_usage":
return "usage";
case "teams":
return "teams";
case "organizations":
return "organizations";
case "users":
return "users";
case "api_ref":
return "api-reference";
case "model-hub-table":
// If you intend the newer in-dashboard page, use "model-hub".
return "model-hub";
case "logs":
return "logs";
case "guardrails":
return "guardrails";
/** History-based navigation to prevent hard reloads / suspense flashes */
function softNavigate(url: string, replace = false) {
if (typeof window === "undefined") return;
if (replace) window.history.replaceState(null, "", url);
else window.history.pushState(null, "", url);
}
/** -------------------------------- */
// tools
case "mcp-servers":
return "tools/mcp-servers";
case "vector-stores":
return "tools/vector-stores";
const Sidebar2: React.FC<SidebarProps> = ({
accessToken,
setPage,
userRole,
defaultSelectedKey,
collapsed = false,
}) => {
const pathname = usePathname();
const { refactoredUIFlag } = useFeatureFlags();
// experimental
case "caching":
return "experimental/caching";
case "prompts":
return "experimental/prompts";
case "budgets":
return "experimental/budgets";
case "transform-request":
return "experimental/api-playground";
case "tag-management":
return "experimental/tag-management";
case "usage": // "Old Usage"
return "experimental/old-usage";
const menuItems: MenuItem[] = [
{ key: "1", page: "api-keys", label: "Virtual Keys", icon: <KeyOutlined style={{ fontSize: "18px" }} /> },
// settings
case "general-settings":
return "settings/router-settings";
case "settings": // "Logging & Alerts"
return "settings/logging-and-alerts";
case "admin-panel":
return "settings/admin-settings";
case "ui-theme":
return "settings/ui-theme";
default:
// treat as already a relative path
return slug.replace(/^\/+/, "");
}
};
/** Prefix base path ("/" or "/ui/") */
const toHref = (slugOrPath: string) => {
const base = getBasePath(); // "/" or "/ui/"
const rel = routeFor(slugOrPath).replace(/^\/+|\/+$/g, "");
return `${base}${rel}`;
};
const Sidebar2: React.FC<SidebarProps> = ({ accessToken, userRole, defaultSelectedKey, collapsed = false }) => {
const router = useRouter();
const pathname = usePathname() || "/";
// ----- Menu config (unchanged labels/icons; same appearance) -----
const menuItems: MenuItemCfg[] = [
{ key: "1", page: "api-keys", label: "Virtual Keys", icon: <KeyOutlined style={{ fontSize: 18 }} /> },
{
key: "3",
page: "llm-playground",
label: "Test Key",
icon: <PlayCircleOutlined style={{ fontSize: "18px" }} />,
icon: <PlayCircleOutlined style={{ fontSize: 18 }} />,
roles: rolesWithWriteAccess,
},
{
key: "2",
page: "models",
label: "Models + Endpoints",
icon: <BlockOutlined style={{ fontSize: "18px" }} />,
icon: <BlockOutlined style={{ fontSize: 18 }} />,
roles: rolesWithWriteAccess,
},
{
key: "12",
page: "new_usage",
label: "Usage",
icon: <BarChartOutlined style={{ fontSize: "18px" }} />,
icon: <BarChartOutlined style={{ fontSize: 18 }} />,
roles: [...all_admin_roles, ...internalUserRoles],
},
{ key: "6", page: "teams", label: "Teams", icon: <TeamOutlined style={{ fontSize: "18px" }} /> },
{ key: "6", page: "teams", label: "Teams", icon: <TeamOutlined style={{ fontSize: 18 }} /> },
{
key: "17",
page: "organizations",
label: "Organizations",
icon: <BankOutlined style={{ fontSize: "18px" }} />,
icon: <BankOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{
key: "5",
page: "users",
label: "Internal Users",
icon: <UserOutlined style={{ fontSize: "18px" }} />,
icon: <UserOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{ key: "14", page: "api_ref", label: "API Reference", icon: <ApiOutlined style={{ fontSize: "18px" }} /> },
{ key: "16", page: "model-hub-table", label: "Model Hub", icon: <AppstoreOutlined style={{ fontSize: "18px" }} /> },
{ key: "15", page: "logs", label: "Logs", icon: <LineChartOutlined style={{ fontSize: "18px" }} /> },
{ key: "14", page: "api_ref", label: "API Reference", icon: <ApiOutlined style={{ fontSize: 18 }} /> },
{
key: "16",
page: "model-hub-table",
label: "Model Hub",
icon: <AppstoreOutlined style={{ fontSize: 18 }} />,
},
{ key: "15", page: "logs", label: "Logs", icon: <LineChartOutlined style={{ fontSize: 18 }} /> },
{
key: "11",
page: "guardrails",
label: "Guardrails",
icon: <SafetyOutlined style={{ fontSize: "18px" }} />,
icon: <SafetyOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{
key: "26",
page: "tools",
label: "Tools",
icon: <ToolOutlined style={{ fontSize: "18px" }} />,
icon: <ToolOutlined style={{ fontSize: 18 }} />,
children: [
{ key: "18", page: "mcp-servers", label: "MCP Servers", icon: <ToolOutlined style={{ fontSize: "18px" }} /> },
{ key: "18", page: "mcp-servers", label: "MCP Servers", icon: <ToolOutlined style={{ fontSize: 18 }} /> },
{
key: "21",
page: "vector-stores",
label: "Vector Stores",
icon: <DatabaseOutlined style={{ fontSize: "18px" }} />,
icon: <DatabaseOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
],
@ -146,136 +212,135 @@ const Sidebar2: React.FC<SidebarProps> = ({
key: "experimental",
page: "experimental",
label: "Experimental",
icon: <ExperimentOutlined style={{ fontSize: "18px" }} />,
icon: <ExperimentOutlined style={{ fontSize: 18 }} />,
children: [
{
key: "9",
page: "caching",
label: "Caching",
icon: <DatabaseOutlined style={{ fontSize: "18px" }} />,
icon: <DatabaseOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{
key: "25",
page: "prompts",
label: "Prompts",
icon: <FileTextOutlined style={{ fontSize: "18px" }} />,
icon: <FileTextOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{
key: "10",
page: "budgets",
label: "Budgets",
icon: <BankOutlined style={{ fontSize: "18px" }} />,
icon: <BankOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{
key: "20",
page: "transform-request",
label: "API Playground",
icon: <ApiOutlined style={{ fontSize: "18px" }} />,
icon: <ApiOutlined style={{ fontSize: 18 }} />,
roles: [...all_admin_roles, ...internalUserRoles],
},
{
key: "19",
page: "tag-management",
label: "Tag Management",
icon: <TagsOutlined style={{ fontSize: "18px" }} />,
icon: <TagsOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{ key: "4", page: "usage", label: "Old Usage", icon: <BarChartOutlined style={{ fontSize: "18px" }} /> },
{ key: "4", page: "usage", label: "Old Usage", icon: <BarChartOutlined style={{ fontSize: 18 }} /> },
],
},
{
key: "settings",
page: "settings",
label: "Settings",
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
icon: <SettingOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
children: [
{
key: "11",
page: "general-settings",
label: "Router Settings",
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
icon: <SettingOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{
key: "8",
page: "settings",
label: "Logging & Alerts",
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
icon: <SettingOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{
key: "13",
page: "admin-panel",
label: "Admin Settings",
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
icon: <SettingOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
{
key: "14",
page: "ui-theme",
label: "UI Theme",
icon: <BgColorsOutlined style={{ fontSize: "18px" }} />,
icon: <SettingOutlined style={{ fontSize: 18 }} />,
roles: all_admin_roles,
},
],
},
];
const findMenuItemKey = (page: string): string => {
const topLevelItem = menuItems.find((item) => item.page === page);
if (topLevelItem) return topLevelItem.key;
for (const item of menuItems) {
// ----- Filter by role without mutating originals -----
const filteredMenuItems = React.useMemo<MenuItemCfg[]>(() => {
return menuItems
.filter((item) => !item.roles || item.roles.includes(userRole))
.map((item) => ({
...item,
children: item.children ? item.children.filter((c) => !c.roles || c.roles.includes(userRole)) : undefined,
}));
}, [userRole]);
// ----- Compute selected key from current path -----
const selectedMenuKey = React.useMemo(() => {
const base = getBasePath();
// strip base prefix and leading slash -> "virtual-keys", "tools/mcp-servers", etc.
const rel = pathname.startsWith(base) ? pathname.slice(base.length) : pathname.replace(/^\/+/, "");
const relLower = rel.toLowerCase();
const matchesPath = (slug: string) => {
const route = routeFor(slug).toLowerCase();
return relLower === route || relLower.startsWith(`${route}/`);
};
// search top-level
for (const item of filteredMenuItems) {
if (!item.children && matchesPath(item.page)) return item.key;
if (item.children) {
const childItem = item.children.find((child) => child.page === page);
if (childItem) return childItem.key;
for (const child of item.children) {
if (matchesPath(child.page)) return child.key;
}
}
}
// fallback to legacy defaultSelectedKey mapping
const fallback = filteredMenuItems.find((i) => i.page === defaultSelectedKey)?.key;
if (fallback) return fallback;
for (const item of filteredMenuItems) {
if (item.children?.some((c) => c.page === defaultSelectedKey)) {
const child = item.children.find((c) => c.page === defaultSelectedKey)!;
return child.key;
}
}
return "1";
};
}, [pathname, filteredMenuItems, defaultSelectedKey]);
const selectedMenuKey = findMenuItemKey(defaultSelectedKey);
const filteredMenuItems = menuItems.filter((item) => {
const hasParentAccess = !item.roles || item.roles.includes(userRole);
if (!hasParentAccess) return false;
if (item.children) {
item.children = item.children.filter((child) => !child.roles || child.roles.includes(userRole));
}
return true;
});
// Helper: update /?page=... under the configured base, WITHOUT triggering App Router nav
const pushToRootWithPage = (page: string, useReplace = false) => {
const params = new URLSearchParams();
params.set("page", page);
const url = withBase(`/?${params.toString()}`);
softNavigate(url, useReplace);
};
const navigateToPage = (page: string) => {
if (page === "api-keys") {
if (refactoredUIFlag) {
// vanity URL, keep SPA alive
softNavigate(withBase("/virtual-keys"));
return; // don't call setPage to keep parity, UI already shows api-keys by default
}
pushToRootWithPage(page);
setPage(page);
return;
}
if (refactoredUIFlag) {
const onVirtualKeys =
typeof window !== "undefined" && window.location.pathname.startsWith(withBase("/virtual-keys"));
pushToRootWithPage(page, onVirtualKeys);
} else {
pushToRootWithPage(page);
}
setPage(page);
// ----- Navigation -----
const goTo = (slug: string) => {
const href = toHref(slug);
router.push(href);
};
return (
@ -287,16 +352,32 @@ const Sidebar2: React.FC<SidebarProps> = ({
collapsedWidth={80}
collapsible
trigger={null}
style={{ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)", position: "relative" }}
style={{
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
position: "relative",
}}
>
<ConfigProvider theme={{ components: { Menu: { iconSize: 18, fontSize: 14 } } }}>
<ConfigProvider
theme={{
components: {
Menu: {
iconSize: 18,
fontSize: 14,
},
},
}}
>
<Menu
mode="inline"
selectedKeys={[selectedMenuKey]}
defaultOpenKeys={collapsed ? [] : ["llm-tools"]}
defaultOpenKeys={collapsed ? [] : ["llm-tools"]} // kept to preserve original appearance
inlineCollapsed={collapsed}
className="custom-sidebar-menu"
style={{ borderRight: 0, backgroundColor: "transparent", fontSize: "14px" }}
style={{
borderRight: 0,
backgroundColor: "transparent",
fontSize: "14px",
}}
items={filteredMenuItems.map((item) => ({
key: item.key,
icon: item.icon,
@ -305,9 +386,9 @@ const Sidebar2: React.FC<SidebarProps> = ({
key: child.key,
icon: child.icon,
label: child.label,
onClick: () => navigateToPage(child.page),
onClick: () => goTo(child.page),
})),
onClick: !item.children ? () => navigateToPage(item.page) : undefined,
onClick: !item.children ? () => goTo(item.page) : undefined,
}))}
/>
</ConfigProvider>

View file

@ -0,0 +1,29 @@
import useFeatureFlags from "@/hooks/useFeatureFlags";
import Sidebar from "@/components/leftnav";
import Sidebar2 from "@/app/(dashboard)/components/Sidebar2";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
interface SidebarProviderProps {
defaultSelectedKey: string;
setPage: (newPage: string) => void;
sidebarCollapsed: boolean;
}
const SidebarProvider = ({ setPage, defaultSelectedKey, sidebarCollapsed }: SidebarProviderProps) => {
const { refactoredUIFlag } = useFeatureFlags();
const { accessToken, userRole } = useAuthorized();
return refactoredUIFlag ? (
<Sidebar2 accessToken={accessToken} defaultSelectedKey={defaultSelectedKey} userRole={userRole} />
) : (
<Sidebar
accessToken={accessToken}
setPage={setPage}
userRole={userRole}
defaultSelectedKey={defaultSelectedKey}
collapsed={sidebarCollapsed}
/>
);
};
export default SidebarProvider;

View file

@ -70,7 +70,7 @@ const CreateUserModal: React.FC<CreateUserModalProps> = ({ possibleUIRoles, onUs
const [isInvitationLinkModalVisible, setIsInvitationLinkModalVisible] = useState(false);
const [invitationLinkData, setInvitationLinkData] = useState<InvitationLink | null>(null);
const [baseUrl, setBaseUrl] = useState<string | null>(null);
const teams = useTeams();
const { teams } = useTeams();
// get all models
useEffect(() => {

View file

@ -0,0 +1,12 @@
"use client";
import TransformRequestPanel from "@/components/transform_request";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const APIPlaygroundPage = () => {
const { accessToken } = useAuthorized();
return <TransformRequestPanel accessToken={accessToken} />;
};
export default APIPlaygroundPage;

View file

@ -0,0 +1,12 @@
"use client";
import BudgetPanel from "@/components/budgets/budget_panel";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const BudgetsPage = () => {
const { accessToken } = useAuthorized();
return <BudgetPanel accessToken={accessToken} />;
};
export default BudgetsPage;

View file

@ -0,0 +1,20 @@
"use client";
import CacheDashboard from "@/components/cache_dashboard";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const CachingPage = () => {
const { token, accessToken, userRole, userId, premiumUser } = useAuthorized();
return (
<CacheDashboard
accessToken={accessToken}
token={token}
userRole={userRole}
userID={userId}
premiumUser={premiumUser}
/>
);
};
export default CachingPage;

View file

@ -0,0 +1,23 @@
"use client";
import Usage from "@/components/usage";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { useState } from "react";
const OldUsagePage = () => {
const { accessToken, token, userRole, userId, premiumUser } = useAuthorized();
const [keys, setKeys] = useState<null | any[]>([]);
return (
<Usage
accessToken={accessToken}
token={token}
userRole={userRole}
userID={userId}
keys={keys}
premiumUser={premiumUser}
/>
);
};
export default OldUsagePage;

View file

@ -0,0 +1,12 @@
"use client";
import PromptsPanel from "@/components/prompts";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const PromptsPage = () => {
const { accessToken } = useAuthorized();
return <PromptsPanel accessToken={accessToken} />;
};
export default PromptsPage;

View file

@ -0,0 +1,12 @@
"use client";
import TagManagement from "@/components/tag_management";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const TagManagementPage = () => {
const { accessToken, userId, userRole } = useAuthorized();
return <TagManagement accessToken={accessToken} userID={userId} userRole={userRole} />;
};
export default TagManagementPage;

View file

@ -0,0 +1,12 @@
"use client";
import GuardrailsPanel from "@/components/guardrails";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const GuardrailsPage = () => {
const { accessToken } = useAuthorized();
return <GuardrailsPanel accessToken={accessToken} />;
};
export default GuardrailsPage;

View file

@ -1,9 +1,9 @@
"use client";
import { useEffect, useMemo } from "react";
import { getCookie, clearTokenCookies } from "@/utils/cookieUtils";
import { useRouter } from "next/navigation";
import { jwtDecode } from "jwt-decode";
import { clearTokenCookies, getCookie } from "@/utils/cookieUtils";
const useAuthorized = () => {
const router = useRouter();
@ -31,10 +31,13 @@ const useAuthorized = () => {
}, [token, router]);
return {
token: token,
accessToken: decoded?.key ?? null,
userId: decoded?.user_id ?? null,
userRole: decoded?.user_role ?? null,
premiumUser: decoded?.premium_user ?? null,
disabledPersonalKeyCreation: decoded?.disabled_non_admin_personal_key_creation ?? null,
showSSOBanner: decoded?.login_method === "username_password" ?? false,
};
};

View file

@ -0,0 +1,28 @@
"use client";
import SpendLogsTable from "@/components/view_logs";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const LogsPage = () => {
const { accessToken, token, userRole, userId, premiumUser } = useAuthorized();
const { teams } = useTeams();
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<SpendLogsTable
accessToken={accessToken}
token={token}
userRole={userRole}
userID={userId}
allTeams={teams || []}
premiumUser={premiumUser}
/>
</QueryClientProvider>
);
};
export default LogsPage;

View file

@ -0,0 +1,12 @@
"use client";
import ModelHubTable from "@/components/model_hub_table";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const ModelHubPage = () => {
const { accessToken, premiumUser, userRole } = useAuthorized();
return <ModelHubTable accessToken={accessToken} publicPage={false} premiumUser={premiumUser} userRole={userRole} />;
};
export default ModelHubPage;

View file

@ -0,0 +1,29 @@
"use client";
import ModelDashboard from "@/components/templates/model_dashboard";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
import { useState } from "react";
const ModelsAndEndpointsPage = () => {
const { token, accessToken, userRole, userId, premiumUser } = useAuthorized();
const [keys, setKeys] = useState<null | any[]>([]);
const { teams } = useTeams();
return (
<ModelDashboard
accessToken={accessToken}
token={token}
userRole={userRole}
userID={userId}
modelData={{ data: [] }}
keys={keys}
setModelData={() => {}}
premiumUser={premiumUser}
teams={teams}
/>
);
};
export default ModelsAndEndpointsPage;

View file

@ -0,0 +1,34 @@
"use client";
import Organizations, { fetchOrganizations } from "@/components/organizations";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { useEffect, useState } from "react";
import { Organization } from "@/components/networking";
import { fetchUserModels } from "@/components/organisms/create_key_button";
const OrganizationsPage = () => {
const { userId: userID, accessToken, userRole, premiumUser } = useAuthorized();
const [organizations, setOrganizations] = useState<Organization[]>([]);
const [userModels, setUserModels] = useState<string[]>([]);
useEffect(() => {
fetchOrganizations(accessToken, setOrganizations).then(() => {});
}, [accessToken]);
useEffect(() => {
fetchUserModels(userID, userRole, accessToken, setUserModels).then(() => {});
}, [userID, userRole, accessToken]);
return (
<Organizations
organizations={organizations}
userRole={userRole}
userModels={userModels}
accessToken={accessToken}
setOrganizations={setOrganizations}
premiumUser={premiumUser}
/>
);
};
export default OrganizationsPage;

View file

@ -0,0 +1,29 @@
"use client";
import AdminPanel from "@/components/admins";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { useState } from "react";
import { Team } from "@/components/key_team_helpers/key_list";
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
const AdminSettings = () => {
const { teams, setTeams } = useTeams();
const [searchParams, setSearchParams] = useState<URLSearchParams>(() =>
typeof window === "undefined" ? new URLSearchParams() : new URLSearchParams(window.location.search),
);
const { accessToken, userId, premiumUser, showSSOBanner } = useAuthorized();
return (
<AdminPanel
searchParams={searchParams}
accessToken={accessToken}
userID={userId}
setTeams={setTeams}
showSSOBanner={showSSOBanner}
premiumUser={premiumUser}
/>
);
};
export default AdminSettings;

View file

@ -0,0 +1,12 @@
"use client";
import Settings from "@/components/settings";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const LoggingAndAlertsPage = () => {
const { accessToken, userRole, userId, premiumUser } = useAuthorized();
return <Settings accessToken={accessToken} userRole={userRole} userID={userId} premiumUser={premiumUser} />;
};
export default LoggingAndAlertsPage;

View file

@ -0,0 +1,12 @@
"use client";
import GeneralSettings from "@/components/general_settings";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const RouterSettingsPage = () => {
const { accessToken, userRole, userId } = useAuthorized();
return <GeneralSettings accessToken={accessToken} userRole={userRole} userID={userId} modelData={{}} />;
};
export default RouterSettingsPage;

View file

@ -0,0 +1,12 @@
"use client";
import UIThemeSettings from "@/components/ui_theme_settings";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const UIThemePage = () => {
const { userId, userRole, accessToken } = useAuthorized();
return <UIThemeSettings userID={userId} userRole={userRole} accessToken={accessToken} />;
};
export default UIThemePage;

View file

@ -0,0 +1,35 @@
"use client";
import Teams from "@/components/teams";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
import { useEffect, useState } from "react";
import { Organization } from "@/components/networking";
import { fetchOrganizations } from "@/components/organizations";
const TeamsPage = () => {
const { accessToken, userId, userRole } = useAuthorized();
const { teams, setTeams } = useTeams();
const [searchParams, setSearchParams] = useState<URLSearchParams>(() =>
typeof window === "undefined" ? new URLSearchParams() : new URLSearchParams(window.location.search),
);
const [organizations, setOrganizations] = useState<Organization[]>([]);
useEffect(() => {
fetchOrganizations(accessToken, setOrganizations).then(() => {});
}, [accessToken]);
return (
<Teams
teams={teams}
searchParams={searchParams}
accessToken={accessToken}
setTeams={setTeams}
userID={userId}
userRole={userRole}
organizations={organizations}
/>
);
};
export default TeamsPage;

View file

@ -0,0 +1,20 @@
"use client";
import ChatUI from "@/components/chat_ui/ChatUI";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const TestKeyPage = () => {
const { token, accessToken, userRole, userId, disabledPersonalKeyCreation } = useAuthorized();
return (
<ChatUI
accessToken={accessToken}
token={token}
userRole={userRole}
userID={userId}
disabledPersonalKeyCreation={disabledPersonalKeyCreation}
/>
);
};
export default TestKeyPage;

View file

@ -0,0 +1,19 @@
"use client";
import { MCPServers } from "@/components/mcp_tools";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const MCPServersPage = () => {
const { accessToken, userRole, userId } = useAuthorized();
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<MCPServers accessToken={accessToken} userRole={userRole} userID={userId} />
</QueryClientProvider>
);
};
export default MCPServersPage;

View file

@ -0,0 +1,12 @@
"use client";
import VectorStoreManagement from "@/components/vector_store_management";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const VectorStoresPage = () => {
const { accessToken, userId, userRole } = useAuthorized();
return <VectorStoreManagement accessToken={accessToken} userID={userId} userRole={userRole} />;
};
export default VectorStoresPage;

View file

@ -0,0 +1,22 @@
"use client";
import NewUsagePage from "@/components/new_usage";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
const UsagePage = () => {
const { accessToken, userRole, userId, premiumUser } = useAuthorized();
const { teams } = useTeams();
return (
<NewUsagePage
accessToken={accessToken}
userRole={userRole}
userID={userId}
teams={teams ?? []}
premiumUser={premiumUser}
/>
);
};
export default UsagePage;

View file

@ -0,0 +1,31 @@
"use client";
import ViewUserDashboard from "@/components/view_users";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import useTeams from "@/app/(dashboard)/virtual-keys/hooks/useTeams";
import { useState } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const UsersPage = () => {
const { accessToken, userRole, userId, token } = useAuthorized();
const [keys, setKeys] = useState<null | any[]>([]);
const { teams } = useTeams();
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<ViewUserDashboard
accessToken={accessToken}
token={token}
keys={keys}
userRole={userRole}
userID={userId}
teams={teams as any}
setKeys={setKeys}
/>
</QueryClientProvider>
);
};
export default UsersPage;

View file

@ -61,7 +61,7 @@ const VirtualKeysTable = ({
setAccessToken,
}: AllKeysTableProps) => {
const { userId: userID, userRole, accessToken, premiumUser } = useAuthorized();
const teams = useTeams();
const { teams } = useTeams();
const [selectedKeyId, setSelectedKeyId] = useState<string | null>(null);
const [userList, setUserList] = useState<UserResponse[]>([]);
const [sorting, setSorting] = React.useState<SortingState>(() => {

View file

@ -4,7 +4,7 @@ import { Team } from "@/components/key_team_helpers/key_list";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const useTeams = () => {
const [teams, setTeams] = useState<Team[]>([]);
const [teams, setTeams] = useState<Team[] | null>([]);
const { accessToken, userId: userID, userRole } = useAuthorized();
useEffect(() => {
@ -14,7 +14,7 @@ const useTeams = () => {
})();
}, [accessToken, userID, userRole]);
return teams;
return { teams, setTeams };
};
export default useTeams;

View file

@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useMemo, useState } from "react";
import React, { Suspense, useEffect, useState } from "react";
import { useSearchParams } from "next/navigation";
import { jwtDecode } from "jwt-decode";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Team } from "@/components/key_team_helpers/key_list";
@ -22,6 +23,7 @@ import ModelHubTable from "@/components/model_hub_table";
import NewUsagePage from "@/components/new_usage";
import APIRef from "@/components/api_ref";
import ChatUI from "@/components/chat_ui/ChatUI";
import Sidebar from "@/components/leftnav";
import Usage from "@/components/usage";
import CacheDashboard from "@/components/cache_dashboard";
import { getUiConfig, proxyBaseUrl, setGlobalLitellmHeaderName } from "@/components/networking";
@ -37,35 +39,50 @@ 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 Sidebar2 from "@/app/(dashboard)/components/Sidebar2";
/** ---- BASE URL HELPERS ---- */
function normalizeBasePrefix(raw: string | undefined | null): string {
const trimmed = (raw ?? "").trim();
if (!trimmed) return "";
const core = trimmed.replace(/^\/+/, "").replace(/\/+$/, "");
return core ? `/${core}` : "";
}
const BASE_PREFIX = normalizeBasePrefix(process.env.NEXT_PUBLIC_BASE_URL);
function withBase(path: string): string {
const p = path.startsWith("/") ? path : `/${path}`;
return `${BASE_PREFIX}${p}` || p;
}
/** -------------------------------- */
import useFeatureFlags from "@/hooks/useFeatureFlags";
import SidebarProvider from "@/app/(dashboard)/components/SidebarProvider";
function getCookie(name: string) {
if (typeof document === "undefined") return null;
const cookieValue = document.cookie.split("; ").find((row) => row.startsWith(name + "="));
return cookieValue ? cookieValue.split("=")[1] : null;
// Safer cookie read + decoding; handles '=' inside values
const match = document.cookie.split("; ").find((row) => row.startsWith(name + "="));
if (!match) return null;
const value = match.slice(name.length + 1);
try {
return decodeURIComponent(value);
} catch {
return value;
}
}
function deleteCookie(name: string, path = "/") {
// Best-effort client-side clear (works for non-HttpOnly cookies without Domain)
document.cookie = `${name}=; Max-Age=0; Path=${path}`;
}
function isJwtExpired(token: string): boolean {
try {
const decoded: any = jwtDecode(token);
if (decoded && typeof decoded.exp === "number") {
return decoded.exp * 1000 <= Date.now();
}
return false;
} catch {
// If we can't decode, treat as invalid/expired
return true;
}
}
function formatUserRole(userRole: string) {
if (!userRole) return "Undefined Role";
if (!userRole) {
return "Undefined Role";
}
switch (userRole.toLowerCase()) {
case "app_owner":
return "App Owner";
case "demo_app_owner":
return "App Owner";
case "app_admin":
return "Admin";
case "proxy_admin":
return "Admin";
case "proxy_admin_viewer":
@ -75,7 +92,7 @@ function formatUserRole(userRole: string) {
case "internal_user":
return "Internal User";
case "internal_user_viewer":
case "internal_viewer":
case "internal_viewer": // TODO:remove if deprecated
return "Internal Viewer";
case "app_user":
return "App User";
@ -95,6 +112,7 @@ function LoadingScreen() {
return (
<div className={cx("h-screen", "flex items-center justify-center gap-4")}>
<div className="text-lg font-medium py-2 pr-4 border-r border-r-gray-200">🚅 LiteLLM</div>
<div className="flex items-center justify-center gap-2">
<UiLoadingSpinner className="size-4" />
<span className="text-gray-600 text-sm">Loading...</span>
@ -103,16 +121,6 @@ function LoadingScreen() {
);
}
/** Derive the app "page" from the URL without triggering App Router navigations */
function getPageFromLocation(loc: Location): string {
const sp = new URLSearchParams(loc.search);
const p = sp.get("page");
if (p) return p;
// vanity route for refactored UI
if (loc.pathname.endsWith("/virtual-keys")) return "api-keys";
return "api-keys";
}
export default function CreateKeyPage() {
const [userRole, setUserRole] = useState("");
const [premiumUser, setPremiumUser] = useState(false);
@ -122,98 +130,156 @@ export default function CreateKeyPage() {
const [keys, setKeys] = useState<null | any[]>([]);
const [organizations, setOrganizations] = useState<Organization[]>([]);
const [userModels, setUserModels] = useState<string[]>([]);
const [proxySettings, setProxySettings] = useState<ProxySettings>({ PROXY_BASE_URL: "", PROXY_LOGOUT_URL: "" });
const [proxySettings, setProxySettings] = useState<ProxySettings>({
PROXY_BASE_URL: "",
PROXY_LOGOUT_URL: "",
});
const [showSSOBanner, setShowSSOBanner] = useState<boolean>(true);
// Stable local mirror of URLSearchParams (no suspense, no flashes)
const [searchParams, setSearchParams] = useState<URLSearchParams>(() =>
typeof window === "undefined" ? new URLSearchParams() : new URLSearchParams(window.location.search),
);
const invitation_id = useMemo(() => searchParams.get("invitation_id"), [searchParams]);
// Local page state (drives UI)
const [page, setPage] = useState<string>(() =>
typeof window === "undefined" ? "api-keys" : getPageFromLocation(window.location),
);
// Keep history/back/forward in sync with UI
useEffect(() => {
const onPop = () => {
if (typeof window === "undefined") return;
setSearchParams(new URLSearchParams(window.location.search));
setPage(getPageFromLocation(window.location));
};
window.addEventListener("popstate", onPop);
return () => window.removeEventListener("popstate", onPop);
}, []);
// Update URL without triggering App Router reload/suspense
const updatePage = (newPage: string) => {
if (typeof window === "undefined") return;
// 1) instant UI update
setPage(newPage);
// 2) URL update under base prefix
const sp = new URLSearchParams(window.location.search);
sp.set("page", newPage);
const url = withBase(`/?${sp.toString()}`);
window.history.pushState(null, "", url);
// 3) keep our local mirror in sync
setSearchParams(new URLSearchParams(sp));
};
const searchParams = useSearchParams()!;
const [modelData, setModelData] = useState<any>({ data: [] });
const [token, setToken] = useState<string | null>(null);
const [createClicked, setCreateClicked] = useState<boolean>(false);
const [authLoading, setAuthLoading] = useState(true);
const [userID, setUserID] = useState<string | null>(null);
const { refactoredUIFlag } = useFeatureFlags();
const invitation_id = searchParams.get("invitation_id");
// Get page from URL, default to 'api-keys' if not present
const [page, setPage] = useState(() => {
return searchParams.get("page") || "api-keys";
});
// Custom setPage function that updates URL
const updatePage = (newPage: string) => {
// Update URL without full page reload
const newSearchParams = new URLSearchParams(searchParams);
newSearchParams.set("page", newPage);
// Use Next.js router to update URL
window.history.pushState(null, "", `?${newSearchParams.toString()}`);
setPage(newPage);
};
const [accessToken, setAccessToken] = useState<string | null>(null);
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const toggleSidebar = () => setSidebarCollapsed((v) => !v);
const toggleSidebar = () => {
setSidebarCollapsed(!sidebarCollapsed);
};
const addKey = (data: any) => {
setKeys((prevData) => (prevData ? [...prevData, data] : [data]));
setCreateClicked((v) => !v);
setCreateClicked(() => !createClicked);
};
const redirectToLogin = authLoading === false && token === null && invitation_id === null;
useEffect(() => {
const token = getCookie("token");
getUiConfig().then(() => {
setToken(token);
setAuthLoading(false);
});
let cancelled = false;
(async () => {
try {
await getUiConfig(); // ensures proxyBaseUrl etc. are ready
} catch {
// proceed regardless; we still need to decide auth state
}
if (cancelled) return;
const raw = getCookie("token");
const valid = raw && !isJwtExpired(raw) ? raw : null;
// If token exists but is invalid/expired, clear it so downstream code
// doesn't keep trying to use it and cause redirect spasms.
if (raw && !valid) {
deleteCookie("token", "/");
}
if (!cancelled) {
setToken(valid);
setAuthLoading(false);
}
})();
return () => {
cancelled = true;
};
}, []);
useEffect(() => {
if (redirectToLogin) {
window.location.href = (proxyBaseUrl || "") + "/sso/key/generate";
// Replace instead of assigning to avoid back-button loops
const dest = (proxyBaseUrl || "") + "/sso/key/generate";
window.location.replace(dest);
}
}, [redirectToLogin]);
useEffect(() => {
if (!token) return;
const decoded = jwtDecode(token) as { [key: string]: any };
if (!decoded) return;
setAccessToken(decoded.key);
setDisabledPersonalKeyCreation(decoded.disabled_non_admin_personal_key_creation);
if (decoded.user_role) {
const formattedUserRole = formatUserRole(decoded.user_role);
setUserRole(formattedUserRole);
if (formattedUserRole === "Admin Viewer") setPage("usage");
if (!token) {
return;
}
// Defensive: re-check expiry in case cookie changed after mount
if (isJwtExpired(token)) {
deleteCookie("token", "/");
setToken(null);
return;
}
let decoded: any = null;
try {
decoded = jwtDecode(token);
} catch {
// Malformed token → treat as unauthenticated
deleteCookie("token", "/");
setToken(null);
return;
}
if (decoded) {
// set accessToken
setAccessToken(decoded.key);
setDisabledPersonalKeyCreation(decoded.disabled_non_admin_personal_key_creation);
// check if userRole is defined
if (decoded.user_role) {
const formattedUserRole = formatUserRole(decoded.user_role);
setUserRole(formattedUserRole);
if (formattedUserRole == "Admin Viewer") {
setPage("usage");
}
}
if (decoded.user_email) {
setUserEmail(decoded.user_email);
}
if (decoded.login_method) {
setShowSSOBanner(decoded.login_method == "username_password" ? true : false);
}
if (decoded.premium_user) {
setPremiumUser(decoded.premium_user);
}
if (decoded.auth_header_name) {
setGlobalLitellmHeaderName(decoded.auth_header_name);
}
if (decoded.user_id) {
setUserID(decoded.user_id);
}
}
if (decoded.user_email) setUserEmail(decoded.user_email);
if (decoded.login_method) setShowSSOBanner(decoded.login_method === "username_password");
if (decoded.premium_user) setPremiumUser(decoded.premium_user);
if (decoded.auth_header_name) setGlobalLitellmHeaderName(decoded.auth_header_name);
if (decoded.user_id) setUserID(decoded.user_id);
}, [token]);
useEffect(() => {
if (accessToken && userID && userRole) {
fetchUserModels(userID, userRole, accessToken, setUserModels);
}
if (accessToken && userID && userRole) {
fetchTeams(accessToken, userID, userRole, null, setTeams);
}
if (accessToken) {
@ -226,194 +292,201 @@ export default function CreateKeyPage() {
}
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider accessToken={accessToken}>
{invitation_id ? (
<UserDashboard
userID={userID}
userRole={userRole}
userEmail={userEmail}
premiumUser={premiumUser}
teams={teams}
keys={keys}
setUserRole={setUserRole}
setUserEmail={setUserEmail}
setTeams={setTeams}
setKeys={setKeys}
organizations={organizations}
addKey={addKey}
createClicked={createClicked}
/>
) : (
<div className="flex flex-col min-h-screen">
<Navbar
<Suspense fallback={<LoadingScreen />}>
<QueryClientProvider client={queryClient}>
<ThemeProvider accessToken={accessToken}>
{invitation_id ? (
<UserDashboard
userID={userID}
userRole={userRole}
premiumUser={premiumUser}
teams={teams}
keys={keys}
setUserRole={setUserRole}
userEmail={userEmail}
setProxySettings={setProxySettings}
proxySettings={proxySettings}
accessToken={accessToken}
isPublicPage={false}
sidebarCollapsed={sidebarCollapsed}
onToggleSidebar={toggleSidebar}
setUserEmail={setUserEmail}
setTeams={setTeams}
setKeys={setKeys}
organizations={organizations}
addKey={addKey}
createClicked={createClicked}
/>
<div className="flex flex-1 overflow-auto">
<div className="mt-2">
<Sidebar2
defaultSelectedKey={page}
setPage={updatePage}
accessToken={accessToken}
userRole={userRole}
/>
</div>
) : (
<div className="flex flex-col min-h-screen">
<Navbar
userID={userID}
userRole={userRole}
premiumUser={premiumUser}
userEmail={userEmail}
setProxySettings={setProxySettings}
proxySettings={proxySettings}
accessToken={accessToken}
isPublicPage={false}
sidebarCollapsed={sidebarCollapsed}
onToggleSidebar={toggleSidebar}
/>
<div className="flex flex-1 overflow-auto">
<div className="mt-2">
<SidebarProvider setPage={updatePage} defaultSelectedKey={page} sidebarCollapsed={sidebarCollapsed} />
</div>
{page === "api-keys" ? (
<UserDashboard
userID={userID}
userRole={userRole}
userEmail={userEmail}
premiumUser={premiumUser}
teams={teams}
keys={keys}
setUserRole={setUserRole}
setUserEmail={setUserEmail}
setTeams={setTeams}
setKeys={setKeys}
organizations={organizations}
addKey={addKey}
createClicked={createClicked}
/>
) : page === "models" ? (
<ModelDashboard
userID={userID}
userRole={userRole}
token={token}
keys={keys}
accessToken={accessToken}
modelData={{ data: [] }}
setModelData={() => {}}
premiumUser={premiumUser}
teams={teams}
/>
) : page === "llm-playground" ? (
<ChatUI
userID={userID}
userRole={userRole}
token={token}
accessToken={accessToken}
disabledPersonalKeyCreation={disabledPersonalKeyCreation}
/>
) : page === "users" ? (
<ViewUserDashboard
userID={userID}
userRole={userRole}
token={token}
keys={keys}
teams={teams}
accessToken={accessToken}
setKeys={setKeys}
/>
) : page === "teams" ? (
<Teams
teams={teams}
setTeams={setTeams}
searchParams={searchParams}
accessToken={accessToken}
userID={userID}
userRole={userRole}
organizations={organizations}
premiumUser={premiumUser}
/>
) : page === "organizations" ? (
<Organizations
organizations={organizations}
setOrganizations={setOrganizations}
userModels={userModels}
accessToken={accessToken}
userRole={userRole}
premiumUser={premiumUser}
/>
) : page === "admin-panel" ? (
<AdminPanel
setTeams={setTeams}
searchParams={searchParams}
accessToken={accessToken}
userID={userID}
showSSOBanner={showSSOBanner}
premiumUser={premiumUser}
proxySettings={proxySettings}
/>
) : page === "api_ref" ? (
<APIRef proxySettings={proxySettings} />
) : page === "settings" ? (
<Settings userID={userID} userRole={userRole} accessToken={accessToken} premiumUser={premiumUser} />
) : page === "budgets" ? (
<BudgetPanel accessToken={accessToken} />
) : page === "guardrails" ? (
<GuardrailsPanel accessToken={accessToken} userRole={userRole} />
) : page === "prompts" ? (
<PromptsPanel accessToken={accessToken} userRole={userRole} />
) : page === "transform-request" ? (
<TransformRequestPanel accessToken={accessToken} />
) : page === "general-settings" ? (
<GeneralSettings userID={userID} userRole={userRole} accessToken={accessToken} modelData={{}} />
) : page === "ui-theme" ? (
<UIThemeSettings userID={userID} userRole={userRole} accessToken={accessToken} />
) : page === "model-hub-table" ? (
<ModelHubTable
accessToken={accessToken}
publicPage={false}
premiumUser={premiumUser}
userRole={userRole}
/>
) : page === "caching" ? (
<CacheDashboard
userID={userID}
userRole={userRole}
token={token}
accessToken={accessToken}
premiumUser={premiumUser}
/>
) : page === "pass-through-settings" ? (
<PassThroughSettings userID={userID} userRole={userRole} accessToken={accessToken} modelData={{}} />
) : page === "logs" ? (
<SpendLogsTable
userID={userID}
userRole={userRole}
token={token}
accessToken={accessToken}
allTeams={(teams as Team[]) ?? []}
premiumUser={premiumUser}
/>
) : page === "mcp-servers" ? (
<MCPServers accessToken={accessToken} userRole={userRole} userID={userID} />
) : page === "tag-management" ? (
<TagManagement accessToken={accessToken} userRole={userRole} userID={userID} />
) : page === "vector-stores" ? (
<VectorStoreManagement accessToken={accessToken} userRole={userRole} userID={userID} />
) : page === "new_usage" ? (
<NewUsagePage
userID={userID}
userRole={userRole}
accessToken={accessToken}
teams={(teams as Team[]) ?? []}
premiumUser={premiumUser}
/>
) : (
<Usage
userID={userID}
userRole={userRole}
token={token}
accessToken={accessToken}
keys={keys}
premiumUser={premiumUser}
/>
)}
{page == "api-keys" ? (
<UserDashboard
userID={userID}
userRole={userRole}
premiumUser={premiumUser}
teams={teams}
keys={keys}
setUserRole={setUserRole}
userEmail={userEmail}
setUserEmail={setUserEmail}
setTeams={setTeams}
setKeys={setKeys}
organizations={organizations}
addKey={addKey}
createClicked={createClicked}
/>
) : page == "models" ? (
<ModelDashboard
userID={userID}
userRole={userRole}
token={token}
keys={keys}
accessToken={accessToken}
modelData={modelData}
setModelData={setModelData}
premiumUser={premiumUser}
teams={teams}
/>
) : page == "llm-playground" ? (
<ChatUI
userID={userID}
userRole={userRole}
token={token}
accessToken={accessToken}
disabledPersonalKeyCreation={disabledPersonalKeyCreation}
/>
) : page == "users" ? (
<ViewUserDashboard
userID={userID}
userRole={userRole}
token={token}
keys={keys}
teams={teams}
accessToken={accessToken}
setKeys={setKeys}
/>
) : page == "teams" ? (
<Teams
teams={teams}
setTeams={setTeams}
searchParams={searchParams}
accessToken={accessToken}
userID={userID}
userRole={userRole}
organizations={organizations}
premiumUser={premiumUser}
/>
) : page == "organizations" ? (
<Organizations
organizations={organizations}
setOrganizations={setOrganizations}
userModels={userModels}
accessToken={accessToken}
userRole={userRole}
premiumUser={premiumUser}
/>
) : page == "admin-panel" ? (
<AdminPanel
setTeams={setTeams}
searchParams={searchParams}
accessToken={accessToken}
userID={userID}
showSSOBanner={showSSOBanner}
premiumUser={premiumUser}
proxySettings={proxySettings}
/>
) : page == "api_ref" ? (
<APIRef proxySettings={proxySettings} />
) : page == "settings" ? (
<Settings userID={userID} userRole={userRole} accessToken={accessToken} premiumUser={premiumUser} />
) : page == "budgets" ? (
<BudgetPanel accessToken={accessToken} />
) : page == "guardrails" ? (
<GuardrailsPanel accessToken={accessToken} userRole={userRole} />
) : page == "prompts" ? (
<PromptsPanel accessToken={accessToken} userRole={userRole} />
) : page == "transform-request" ? (
<TransformRequestPanel accessToken={accessToken} />
) : page == "general-settings" ? (
<GeneralSettings
userID={userID}
userRole={userRole}
accessToken={accessToken}
modelData={modelData}
/>
) : page == "ui-theme" ? (
<UIThemeSettings userID={userID} userRole={userRole} accessToken={accessToken} />
) : page == "model-hub-table" ? (
<ModelHubTable
accessToken={accessToken}
publicPage={false}
premiumUser={premiumUser}
userRole={userRole}
/>
) : page == "caching" ? (
<CacheDashboard
userID={userID}
userRole={userRole}
token={token}
accessToken={accessToken}
premiumUser={premiumUser}
/>
) : page == "pass-through-settings" ? (
<PassThroughSettings
userID={userID}
userRole={userRole}
accessToken={accessToken}
modelData={modelData}
/>
) : page == "logs" ? (
<SpendLogsTable
userID={userID}
userRole={userRole}
token={token}
accessToken={accessToken}
allTeams={(teams as Team[]) ?? []}
premiumUser={premiumUser}
/>
) : page == "mcp-servers" ? (
<MCPServers accessToken={accessToken} userRole={userRole} userID={userID} />
) : page == "tag-management" ? (
<TagManagement accessToken={accessToken} userRole={userRole} userID={userID} />
) : page == "vector-stores" ? (
<VectorStoreManagement accessToken={accessToken} userRole={userRole} userID={userID} />
) : page == "new_usage" ? (
<NewUsagePage
userID={userID}
userRole={userRole}
accessToken={accessToken}
teams={(teams as Team[]) ?? []}
premiumUser={premiumUser}
/>
) : (
<Usage
userID={userID}
userRole={userRole}
token={token}
accessToken={accessToken}
keys={keys}
premiumUser={premiumUser}
/>
)}
</div>
</div>
</div>
)}
</ThemeProvider>
</QueryClientProvider>
)}
</ThemeProvider>
</QueryClientProvider>
</Suspense>
);
}