= {}): Team => ({
- team_id: "team-1",
- team_alias: "Engineering",
- models,
- max_budget: null,
- budget_duration: null,
- tpm_limit: null,
- rpm_limit: null,
- organization_id: "org-1",
- created_at: "2024-01-01T00:00:00Z",
- keys: [],
- members_with_roles: [],
- spend: 0,
- ...overrides,
-});
-
-// Wrap in a table so the from TableCell renders without HTML warnings.
-const renderModelsCell = (team: Team) =>
- render(
- ,
- );
-
-describe("ModelsCell", () => {
- it("should show 'All Proxy Models' badge when the models array is empty", () => {
- renderModelsCell(makeTeam([]));
-
- expect(screen.getByText("All Proxy Models")).toBeInTheDocument();
- });
-
- it("should show an 'All Proxy Models' badge when the model value is 'all-proxy-models'", () => {
- renderModelsCell(makeTeam(["all-proxy-models"]));
-
- expect(screen.getByText("All Proxy Models")).toBeInTheDocument();
- });
-
- it("should display individual model badges for up to 3 models without an accordion", () => {
- renderModelsCell(makeTeam(["gpt-4", "gpt-3.5-turbo", "claude-3"]));
-
- expect(screen.getByText("gpt-4")).toBeInTheDocument();
- expect(screen.getByText("gpt-3.5-turbo")).toBeInTheDocument();
- expect(screen.getByText("claude-3")).toBeInTheDocument();
- expect(screen.queryByRole("button", { name: /accordion/i })).not.toBeInTheDocument();
- });
-
- it("should truncate model names longer than 30 characters with an ellipsis", () => {
- const longName = "a-very-long-model-name-exceeding-thirty-chars";
- renderModelsCell(makeTeam([longName]));
-
- const badge = screen.getByText((text) => text.endsWith("..."));
- expect(badge).toBeInTheDocument();
- expect(badge.textContent!.length).toBeLessThanOrEqual(33); // 30 chars + "..."
- });
-
- it("should show the first 3 models and a '+N more models' badge when there are more than 3 models", () => {
- renderModelsCell(makeTeam(["m1", "m2", "m3", "m4", "m5"]));
-
- expect(screen.getByText("m1")).toBeInTheDocument();
- expect(screen.getByText("m2")).toBeInTheDocument();
- expect(screen.getByText("m3")).toBeInTheDocument();
- expect(screen.getByText("+2 more models")).toBeInTheDocument();
- expect(screen.queryByText("m4")).not.toBeInTheDocument();
- expect(screen.queryByText("m5")).not.toBeInTheDocument();
- });
-
- it("should use singular 'more model' when there is exactly 1 overflow model", () => {
- renderModelsCell(makeTeam(["m1", "m2", "m3", "m4"]));
-
- expect(screen.getByText("+1 more model")).toBeInTheDocument();
- });
-
- it("should show the accordion toggle button when there are more than 3 models", () => {
- renderModelsCell(makeTeam(["m1", "m2", "m3", "m4"]));
-
- expect(screen.getByRole("button", { name: /accordion/i })).toBeInTheDocument();
- });
-
- it("should expand to show all models when the accordion toggle is clicked", () => {
- renderModelsCell(makeTeam(["m1", "m2", "m3", "m4", "m5"]));
-
- act(() => {
- screen.getByRole("button", { name: /accordion/i }).click();
- });
-
- expect(screen.getByText("m4")).toBeInTheDocument();
- expect(screen.getByText("m5")).toBeInTheDocument();
- expect(screen.queryByText("+2 more models")).not.toBeInTheDocument();
- });
-
- it("should collapse back to show the overflow badge after a second click on the toggle", () => {
- renderModelsCell(makeTeam(["m1", "m2", "m3", "m4", "m5"]));
-
- const toggle = screen.getByRole("button", { name: /accordion/i });
- act(() => {
- toggle.click();
- });
- act(() => {
- toggle.click();
- });
-
- expect(screen.queryByText("m4")).not.toBeInTheDocument();
- expect(screen.getByText("+2 more models")).toBeInTheDocument();
- });
-
- it("should collapse to a single 'All Proxy Models' badge when the models list includes 'all-proxy-models'", () => {
- renderModelsCell(makeTeam(["m1", "m2", "m3", "all-proxy-models"]));
-
- // When all-proxy-models is present, all individual models are hidden and no accordion is shown
- expect(screen.getByText("All Proxy Models")).toBeInTheDocument();
- expect(screen.queryByText("m1")).not.toBeInTheDocument();
- expect(screen.queryByText("m2")).not.toBeInTheDocument();
- expect(screen.queryByText("m3")).not.toBeInTheDocument();
- expect(screen.queryByRole("button", { name: /accordion/i })).not.toBeInTheDocument();
- });
-});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/ModelsCell.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/ModelsCell.tsx
deleted file mode 100644
index 62a7fdb783..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/ModelsCell.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import { Badge, Icon, TableCell, Text } from "@tremor/react";
-import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline";
-import { getModelDisplayName } from "@/components/key_team_helpers/fetch_available_models_team_key";
-import React, { useMemo, useState } from "react";
-import { Team } from "@/components/key_team_helpers/key_list";
-
-interface ModelsCellProps {
- team: Team;
-}
-
-interface ModelEntry {
- name: string;
- source: "direct" | "access_group";
-}
-
-const ModelsCell = ({ team }: ModelsCellProps) => {
- const [expandedAccordion, setExpandedAccordion] = useState(false);
-
- const isAllModels = !team.models || team.models.length === 0 || team.models.includes("all-proxy-models");
-
- const modelEntries: ModelEntry[] = useMemo(() => {
- if (isAllModels) return [];
- const entries: ModelEntry[] = team.models.map((m) => ({
- name: m,
- source: "direct" as const,
- }));
- for (const m of team.access_group_models || []) {
- entries.push({ name: m, source: "access_group" });
- }
- return entries;
- }, [team.models, team.access_group_models, isAllModels]);
-
- const renderBadge = (entry: ModelEntry, index: number) => {
- if (entry.name === "all-proxy-models") {
- return (
-
- All Proxy Models
-
- );
- }
- const displayName = getModelDisplayName(entry.name);
- const truncated = displayName.length > 30 ? `${displayName.slice(0, 30)}...` : displayName;
- return (
-
- {truncated}
-
- );
- };
-
- return (
- 3 ? "px-0" : ""}
- >
-
- {modelEntries.length === 0 ? (
-
- All Proxy Models
-
- ) : (
-
-
- {modelEntries.length > 3 && (
-
- {
- setExpandedAccordion((prev) => !prev);
- }}
- />
-
- )}
-
- {modelEntries.slice(0, 3).map((entry, index) => renderBadge(entry, index))}
- {modelEntries.length > 3 && !expandedAccordion && (
-
-
- +{modelEntries.length - 3} {modelEntries.length - 3 === 1 ? "more model" : "more models"}
-
-
- )}
- {expandedAccordion && (
-
- {modelEntries.slice(3).map((entry, index) => renderBadge(entry, index + 3))}
-
- )}
-
-
-
- )}
-
-
- );
-};
-
-export default ModelsCell;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/TeamsTable.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/TeamsTable.test.tsx
deleted file mode 100644
index 6b072ababb..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/TeamsTable.test.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import { render, screen } from "@testing-library/react";
-import userEvent from "@testing-library/user-event";
-import React from "react";
-import { describe, expect, it, vi } from "vitest";
-import { Team } from "@/components/key_team_helpers/key_list";
-import TeamsTable from "./TeamsTable";
-
-vi.mock("@tremor/react", () => ({
- Button: React.forwardRef(({ children, ...props }, ref) =>
- React.createElement("button", { ...props, ref }, children),
- ),
- Icon: ({ onClick, ...props }: any) => ,
- Table: ({ children }: any) => ,
- TableHead: ({ children }: any) => {children},
- TableBody: ({ children }: any) => {children},
- TableRow: ({ children }: any) => {children} ,
- TableHeaderCell: ({ children }: any) => {children} | ,
- TableCell: ({ children, ...props }: any) => {children} | ,
- Text: ({ children }: any) => {children},
-}));
-
-vi.mock("antd", () => ({
- Tooltip: ({ children }: any) => <>{children}>,
-}));
-
-vi.mock("@heroicons/react/outline", () => ({
- PencilAltIcon: () => ,
- TrashIcon: () => ,
-}));
-
-vi.mock("@/utils/dataUtils", () => ({
- formatNumberWithCommas: (val: number, decimals: number) =>
- val != null ? val.toFixed(decimals) : "N/A",
-}));
-
-vi.mock("@/app/(dashboard)/teams/components/TeamsTable/ModelsCell", () => ({
- default: ({ team }: any) => {team.models.join(",")} | ,
-}));
-
-vi.mock("@/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/YourRoleCell", () => ({
- default: ({ team }: any) => {team.team_id} | ,
-}));
-
-const makeTeam = (overrides: Partial = {}): Team => ({
- team_id: "team-abc1234",
- team_alias: "Platform",
- models: ["gpt-4"],
- max_budget: 500,
- budget_duration: null,
- tpm_limit: null,
- rpm_limit: null,
- organization_id: "org-1",
- created_at: "2024-06-01T00:00:00Z",
- keys: [],
- members_with_roles: [],
- spend: 123.4567,
- ...overrides,
-});
-
-const defaultPerTeamInfo = {
- "team-abc1234": {
- keys: [{ token: "tok-1" } as any, { token: "tok-2" } as any],
- team_info: {
- members_with_roles: [{ user_id: "u1", role: "admin" } as any],
- },
- },
-};
-
-const renderTable = (overrides: Partial[0]> = {}) => {
- const defaults = {
- teams: [makeTeam()],
- currentOrg: null,
- perTeamInfo: defaultPerTeamInfo,
- userRole: "Admin",
- userId: "user-1",
- setSelectedTeamId: vi.fn(),
- setEditTeam: vi.fn(),
- onDeleteTeam: vi.fn(),
- };
- return render();
-};
-
-describe("TeamsTable", () => {
- it("should render table headers", () => {
- renderTable();
-
- expect(screen.getByText("Team Name")).toBeInTheDocument();
- expect(screen.getByText("Team ID")).toBeInTheDocument();
- expect(screen.getByText("Created")).toBeInTheDocument();
- expect(screen.getByText("Spend (USD)")).toBeInTheDocument();
- expect(screen.getByText("Budget (USD)")).toBeInTheDocument();
- expect(screen.getByText("Models")).toBeInTheDocument();
- expect(screen.getByText("Organization")).toBeInTheDocument();
- expect(screen.getByText("Your Role")).toBeInTheDocument();
- expect(screen.getByText("Info")).toBeInTheDocument();
- });
-
- it("should render team rows with team data", () => {
- renderTable();
-
- expect(screen.getByText("Platform")).toBeInTheDocument();
- expect(screen.getByText("team-ab...")).toBeInTheDocument();
- expect(screen.getByText("org-1")).toBeInTheDocument();
- });
-
- it("should show edit and delete icons for Admin users", () => {
- renderTable({ userRole: "Admin" });
-
- expect(screen.getAllByTestId("icon-btn").length).toBeGreaterThanOrEqual(2);
- });
-
- it("should not show edit and delete icons for non-Admin users", () => {
- renderTable({ userRole: "Internal User" });
-
- // Only the team ID button should be present, no icon-btn for edit/delete
- const iconBtns = screen.queryAllByTestId("icon-btn");
- expect(iconBtns).toHaveLength(0);
- });
-
- it("should call setSelectedTeamId when team ID button is clicked", async () => {
- const user = userEvent.setup();
- const setSelectedTeamId = vi.fn();
- renderTable({ setSelectedTeamId });
-
- await user.click(screen.getByText("team-ab..."));
-
- expect(setSelectedTeamId).toHaveBeenCalledWith("team-abc1234");
- });
-});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/TeamsTable.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/TeamsTable.tsx
deleted file mode 100644
index f881065d4a..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/TeamsTable.tsx
+++ /dev/null
@@ -1,166 +0,0 @@
-import { Button, Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text } from "@tremor/react";
-import { Tooltip } from "antd";
-import { formatNumberWithCommas } from "@/utils/dataUtils";
-import { PencilAltIcon, TrashIcon } from "@heroicons/react/outline";
-import React from "react";
-import { type KeyResponse, Team } from "@/components/key_team_helpers/key_list";
-import { Member, Organization } from "@/components/networking";
-import ModelsCell from "@/app/(dashboard)/teams/components/TeamsTable/ModelsCell";
-import YourRoleCell from "@/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/YourRoleCell";
-
-type TeamsTableProps = {
- teams: Team[] | null;
- currentOrg: Organization | null;
- perTeamInfo: Record;
- userRole: string | null;
- userId: string | null;
- setSelectedTeamId: (teamId: string) => void;
- setEditTeam: (editTeam: boolean) => void;
- onDeleteTeam: (teamId: string) => void;
-};
-
-interface TeamInfo {
- members_with_roles: Member[];
-}
-
-interface PerTeamInfo {
- keys: KeyResponse[];
- team_info: TeamInfo;
-}
-
-const TeamsTable = ({
- teams,
- currentOrg,
- setSelectedTeamId,
- perTeamInfo,
- userRole,
- userId,
- setEditTeam,
- onDeleteTeam,
-}: TeamsTableProps) => {
- return (
-
-
-
- Team Name
- Team ID
- Created
- Spend (USD)
- Budget (USD)
- Models
- Organization
- Your Role
- Info
-
-
-
-
- {teams && teams.length > 0
- ? teams
- .filter((team) => {
- if (!currentOrg) return true;
- return team.organization_id === currentOrg.organization_id;
- })
- .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
- .map((team: any) => (
-
-
- {team["team_alias"]}
-
-
-
-
-
-
-
-
-
- {team.created_at ? new Date(team.created_at).toLocaleDateString() : "N/A"}
-
-
- {formatNumberWithCommas(team["spend"], 4)}
-
-
- {team["max_budget"] !== null && team["max_budget"] !== undefined ? team["max_budget"] : "No limit"}
-
-
- {team.organization_id}
-
-
-
- {perTeamInfo &&
- team.team_id &&
- perTeamInfo[team.team_id] &&
- perTeamInfo[team.team_id].keys &&
- perTeamInfo[team.team_id].keys.length}{" "}
- Keys
-
-
- {perTeamInfo &&
- team.team_id &&
- perTeamInfo[team.team_id] &&
- perTeamInfo[team.team_id].team_info &&
- perTeamInfo[team.team_id].team_info.members_with_roles &&
- perTeamInfo[team.team_id].team_info.members_with_roles.length}{" "}
- Members
-
-
-
- {userRole == "Admin" ? (
- <>
- {
- setSelectedTeamId(team.team_id);
- setEditTeam(true);
- }}
- />
- onDeleteTeam(team.team_id)} icon={TrashIcon} size="sm" />
- >
- ) : null}
-
-
- ))
- : null}
-
-
- );
-};
-
-export default TeamsTable;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/TeamRoleBadge.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/TeamRoleBadge.test.tsx
deleted file mode 100644
index b7e659403c..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/TeamRoleBadge.test.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from "react";
-import { describe, it, expect } from "vitest";
-import { render, screen } from "@testing-library/react";
-import "@testing-library/jest-dom";
-import TeamRoleBadge from "./TeamRoleBadge";
-
-const renderBadge = (role: string | null) => render({TeamRoleBadge(role)} );
-
-describe("TeamRoleBadge", () => {
- it("renders admin badge with correct label, base classes, styles, and an icon", () => {
- renderBadge("admin");
- const label = screen.getByText("Admin");
- const badge = label.closest("span")!;
- expect(badge).toHaveClass("inline-flex", "items-center", "border", "text-xs", "font-medium");
- expect(badge).toHaveStyle({
- backgroundColor: "#EEF2FF",
- color: "#3730A3",
- borderColor: "#C7D2FE",
- });
- expect(badge.querySelector("svg")).toBeInTheDocument(); // ShieldIcon renders as an SVG
- });
-
- it.each<[string | null]>([["user"], [null], ["viewer" as unknown as string]])(
- "renders member badge for non-admin role (%p) with correct styles",
- (role) => {
- renderBadge(role);
- const label = screen.getByText("Member");
- const badge = label.closest("span")!;
- expect(badge).toHaveClass("inline-flex", "items-center", "border", "text-xs", "font-medium");
- expect(badge).toHaveStyle({
- backgroundColor: "#F3F4F6",
- color: "#4B5563",
- borderColor: "#E5E7EB",
- });
- expect(badge.querySelector("svg")).toBeInTheDocument(); // UserIcon renders as an SVG
- },
- );
-});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/TeamRoleBadge.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/TeamRoleBadge.tsx
deleted file mode 100644
index 394b5d8534..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/TeamRoleBadge.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { ShieldIcon, UserIcon } from "lucide-react";
-
-const MEMBER_BADGE_BG = "#F3F4F6"; // gray-100
-const MEMBER_BADGE_TEXT = "#4B5563"; // gray-600
-const MEMBER_BADGE_BORDER = "#E5E7EB"; // gray-200
-
-const ADMIN_BADGE_BG = "#EEF2FF"; // indigo-50
-const ADMIN_BADGE_TEXT = "#3730A3"; // indigo-800
-const ADMIN_BADGE_BORDER = "#C7D2FE"; // indigo-200
-
-const TeamRoleBadge = (role: string | null) => {
- const base = "inline-flex items-center px-2.5 py-0.5 rounded-md text-xs font-medium border";
-
- switch (role) {
- case "admin":
- return (
-
-
- Admin
-
- );
- case "user":
- default:
- return (
-
-
- Member
-
- );
- }
-};
-
-export default TeamRoleBadge;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/YourRoleCell.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/YourRoleCell.test.tsx
deleted file mode 100644
index 20a4497159..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/YourRoleCell.test.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from "react";
-import { describe, it, expect, vi } from "vitest";
-import { render, screen } from "@testing-library/react";
-import "@testing-library/jest-dom";
-import type { Team } from "@/components/key_team_helpers/key_list";
-import YourRoleCell from "./YourRoleCell";
-
-// Lightweight mocks for stable, focused tests
-vi.mock("@tremor/react", () => ({
- TableCell: ({ children }: { children: React.ReactNode }) => {children} ,
-}));
-
-// The component invokes TeamRoleBadge as a function, so mock it as such
-vi.mock("@/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/TeamRoleBadge", () => ({
- __esModule: true,
- default: (role: string | null) => {role === "admin" ? "Admin" : "Member"},
-}));
-
-const team = (members?: Array<{ user_id: string; role: "admin" | "user" }>): Team =>
- ({ members_with_roles: members }) as unknown as Team;
-
-describe("YourRoleCell", () => {
- it("renders Admin when the user is an admin of the team", () => {
- render();
- expect(screen.getByTestId("cell")).toBeInTheDocument();
- expect(screen.getByTestId("badge")).toHaveTextContent("Admin");
- });
-
- it("renders Member when the user is a regular member", () => {
- render();
- expect(screen.getByTestId("badge")).toHaveTextContent("Member");
- });
-
- it.each<[string, Team, string | null]>([
- ["userId is null", team([{ user_id: "u3", role: "admin" }]), null],
- ["user not in team", team([{ user_id: "x", role: "user" }]), "y"],
- ["team has no members", team([]), "u4"],
- ["members field undefined", team(undefined), "u5"],
- ])("falls back to Member when no role can be determined (%s)", (_label, t, uid) => {
- render();
- expect(screen.getByTestId("badge")).toHaveTextContent("Member");
- });
-});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/YourRoleCell.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/YourRoleCell.tsx
deleted file mode 100644
index 66592943fd..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/YourRoleCell.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { TableCell } from "@tremor/react";
-import { Team } from "@/components/key_team_helpers/key_list";
-import TeamRoleBadge from "@/app/(dashboard)/teams/components/TeamsTable/YourRoleCell/TeamRoleBadge";
-
-interface YourRoleCellProps {
- team: Team;
- userId: string | null;
-}
-
-const getUserRole = (team: Team, userId: string | null): string | null => {
- if (!userId) return null;
- const member = team.members_with_roles?.find((m) => m.user_id === userId);
- return member?.role ?? null;
-};
-
-const YourRoleCell = ({ team, userId }: YourRoleCellProps) => {
- const roleBadge = TeamRoleBadge(getUserRole(team, userId));
-
- return {roleBadge};
-};
-
-export default YourRoleCell;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx
deleted file mode 100644
index 1a8c6632a0..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx
+++ /dev/null
@@ -1,822 +0,0 @@
-import { Button as Button2, Form, Input, Modal, Select as Select2, Switch, Tooltip } from "antd";
-import { Accordion, AccordionBody, AccordionHeader, Text, TextInput } from "@tremor/react";
-import { InfoCircleOutlined } from "@ant-design/icons";
-import {
- fetchAvailableModelsForTeamOrKey,
- getModelDisplayName,
- unfurlWildcardModelsInList,
-} from "@/components/key_team_helpers/fetch_available_models_team_key";
-import NumericalInput from "@/components/shared/numerical_input";
-import VectorStoreSelector from "@/components/vector_store_management/VectorStoreSelector";
-import MCPServerSelector from "@/components/mcp_server_management/MCPServerSelector";
-import AgentSelector from "@/components/agent_management/AgentSelector";
-import PremiumLoggingSettings from "@/components/common_components/PremiumLoggingSettings";
-import ModelAliasManager from "@/components/common_components/ModelAliasManager";
-import React, { useEffect, useState } from "react";
-import { useQueryClient } from "@tanstack/react-query";
-import NotificationsManager from "@/components/molecules/notifications_manager";
-import {
- fetchMCPAccessGroups,
- getGuardrailsList,
- getPoliciesList,
- Organization,
- Team,
- teamCreateCall,
-} from "@/components/networking";
-import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-import { organizationKeys } from "@/app/(dashboard)/hooks/organizations/useOrganizations";
-import MCPToolPermissions from "@/components/mcp_server_management/MCPToolPermissions";
-import SearchToolSelector from "@/components/SearchTools/SearchToolSelector";
-
-interface ModelAliases {
- [key: string]: string;
-}
-
-interface CreateTeamModalProps {
- isTeamModalVisible: boolean;
- handleOk: () => void;
- handleCancel: () => void;
- currentOrg: Organization | null;
- organizations: Organization[] | null;
- teams: Team[] | null;
- setTeams: (teams: Team[] | null) => void;
- modelAliases: ModelAliases;
- setModelAliases: (modelAliases: ModelAliases) => void;
- loggingSettings: any[];
- setLoggingSettings: (loggingSettings: any[]) => void;
- setIsTeamModalVisible: (isTeamModalVisible: boolean) => void;
-}
-
-const getOrganizationModels = (organization: Organization | null, userModels: string[]) => {
- let tempModelsToPick = [];
-
- if (organization) {
- if (organization.models.length > 0) {
- console.log(`organization.models: ${organization.models}`);
- tempModelsToPick = organization.models;
- } else {
- // show all available models if the team has no models set
- tempModelsToPick = userModels;
- }
- } else {
- // no team set, show all available models
- tempModelsToPick = userModels;
- }
-
- return unfurlWildcardModelsInList(tempModelsToPick, userModels);
-};
-
-const CreateTeamModal = ({
- isTeamModalVisible,
- handleOk,
- handleCancel,
- currentOrg,
- organizations,
- teams,
- setTeams,
- modelAliases,
- setModelAliases,
- loggingSettings,
- setLoggingSettings,
- setIsTeamModalVisible,
-}: CreateTeamModalProps) => {
- const { userId: userID, userRole, accessToken, premiumUser } = useAuthorized();
- const queryClient = useQueryClient();
- const [form] = Form.useForm();
- const [userModels, setUserModels] = useState([]);
- const [currentOrgForCreateTeam, setCurrentOrgForCreateTeam] = useState(null);
- const [modelsToPick, setModelsToPick] = useState([]);
- const [guardrailsList, setGuardrailsList] = useState([]);
- const [policiesList, setPoliciesList] = useState([]);
- const [mcpAccessGroups, setMcpAccessGroups] = useState([]);
- const [mcpAccessGroupsLoaded, setMcpAccessGroupsLoaded] = useState(false);
-
- useEffect(() => {
- const fetchUserModels = async () => {
- try {
- if (userID === null || userRole === null || accessToken === null) {
- return;
- }
- const models = await fetchAvailableModelsForTeamOrKey(userID, userRole, accessToken);
- if (models) {
- setUserModels(models);
- }
- } catch (error) {
- console.error("Error fetching user models:", error);
- }
- };
-
- fetchUserModels();
- }, [accessToken, userID, userRole, teams]);
-
- useEffect(() => {
- console.log(`currentOrgForCreateTeam: ${currentOrgForCreateTeam}`);
- const models = getOrganizationModels(currentOrgForCreateTeam, userModels);
- console.log(`models: ${models}`);
- setModelsToPick(models);
- form.setFieldValue("models", []);
- }, [currentOrgForCreateTeam, userModels, form]);
-
- const fetchMcpAccessGroups = async () => {
- try {
- if (accessToken == null) {
- return;
- }
- const groups = await fetchMCPAccessGroups(accessToken);
- setMcpAccessGroups(groups);
- } catch (error) {
- console.error("Failed to fetch MCP access groups:", error);
- }
- };
-
- useEffect(() => {
- fetchMcpAccessGroups();
- }, [accessToken, fetchMcpAccessGroups]);
-
- useEffect(() => {
- const fetchGuardrails = async () => {
- try {
- if (accessToken == null) {
- return;
- }
-
- const response = await getGuardrailsList(accessToken);
- const guardrailNames = response.guardrails.map((g: { guardrail_name: string }) => g.guardrail_name);
- setGuardrailsList(guardrailNames);
- } catch (error) {
- console.error("Failed to fetch guardrails:", error);
- }
- };
-
- const fetchPolicies = async () => {
- try {
- if (accessToken == null) {
- return;
- }
-
- const response = await getPoliciesList(accessToken);
- const policyNames = response.policies.map((p: { policy_name: string }) => p.policy_name);
- setPoliciesList(policyNames);
- } catch (error) {
- console.error("Failed to fetch policies:", error);
- }
- };
-
- fetchGuardrails();
- fetchPolicies();
- }, [accessToken]);
-
- const handleCreate = async (formValues: Record) => {
- try {
- console.log(`formValues: ${JSON.stringify(formValues)}`);
- if (accessToken != null) {
- const newTeamAlias = formValues?.team_alias;
- const existingTeamAliases = teams?.map((t) => t.team_alias) ?? [];
- let organizationId = formValues?.organization_id || currentOrg?.organization_id;
- if (organizationId === "" || typeof organizationId !== "string") {
- formValues.organization_id = null;
- } else {
- formValues.organization_id = organizationId.trim();
- }
-
- // Remove guardrails from top level since it's now in metadata
- if (existingTeamAliases.includes(newTeamAlias)) {
- throw new Error(`Team alias ${newTeamAlias} already exists, please pick another alias`);
- }
-
- NotificationsManager.info("Creating Team");
-
- // Handle logging settings in metadata
- if (loggingSettings.length > 0) {
- let metadata = {};
- if (formValues.metadata) {
- try {
- metadata = JSON.parse(formValues.metadata);
- } catch (e) {
- console.warn("Invalid JSON in metadata field, starting with empty object");
- }
- }
-
- // Add logging settings to metadata
- metadata = {
- ...metadata,
- logging: loggingSettings.filter((config) => config.callback_name), // Only include configs with callback_name
- };
-
- formValues.metadata = JSON.stringify(metadata);
- }
-
- if (formValues.secret_manager_settings) {
- if (typeof formValues.secret_manager_settings === "string") {
- if (formValues.secret_manager_settings.trim() === "") {
- delete formValues.secret_manager_settings;
- } else {
- try {
- formValues.secret_manager_settings = JSON.parse(formValues.secret_manager_settings);
- } catch (e) {
- throw new Error("Failed to parse secret manager settings: " + e);
- }
- }
- }
- }
-
- // Transform integrations into object_permission (vector stores, MCP, agents, search tools)
- const hasAgents =
- formValues.allowed_agents_and_groups &&
- ((formValues.allowed_agents_and_groups.agents?.length ?? 0) > 0 ||
- (formValues.allowed_agents_and_groups.accessGroups?.length ?? 0) > 0);
- const hasSearchTools =
- Array.isArray(formValues.object_permission_search_tools) &&
- formValues.object_permission_search_tools.length > 0;
-
- if (
- (formValues.allowed_vector_store_ids && formValues.allowed_vector_store_ids.length > 0) ||
- (formValues.allowed_mcp_servers_and_groups &&
- (formValues.allowed_mcp_servers_and_groups.servers?.length > 0 ||
- formValues.allowed_mcp_servers_and_groups.accessGroups?.length > 0 ||
- formValues.allowed_mcp_servers_and_groups.toolPermissions)) ||
- hasAgents ||
- hasSearchTools
- ) {
- if (!formValues.object_permission) {
- formValues.object_permission = {};
- }
- if (formValues.allowed_vector_store_ids && formValues.allowed_vector_store_ids.length > 0) {
- formValues.object_permission.vector_stores = formValues.allowed_vector_store_ids;
- delete formValues.allowed_vector_store_ids;
- }
- if (formValues.allowed_mcp_servers_and_groups) {
- const { servers, accessGroups } = formValues.allowed_mcp_servers_and_groups;
- if (servers && servers.length > 0) {
- formValues.object_permission.mcp_servers = servers;
- }
- if (accessGroups && accessGroups.length > 0) {
- formValues.object_permission.mcp_access_groups = accessGroups;
- }
- delete formValues.allowed_mcp_servers_and_groups;
- }
-
- // Add tool permissions separately
- if (formValues.mcp_tool_permissions && Object.keys(formValues.mcp_tool_permissions).length > 0) {
- formValues.object_permission.mcp_tool_permissions = formValues.mcp_tool_permissions;
- delete formValues.mcp_tool_permissions;
- }
-
- // Handle agent permissions
- if (formValues.allowed_agents_and_groups) {
- const { agents, accessGroups } = formValues.allowed_agents_and_groups;
- if (agents && agents.length > 0) {
- formValues.object_permission.agents = agents;
- }
- if (accessGroups && accessGroups.length > 0) {
- formValues.object_permission.agent_access_groups = accessGroups;
- }
- delete formValues.allowed_agents_and_groups;
- }
-
- if (hasSearchTools) {
- formValues.object_permission.search_tools = formValues.object_permission_search_tools;
- delete formValues.object_permission_search_tools;
- }
- }
-
- // Transform allowed_mcp_access_groups into object_permission
- if (formValues.allowed_mcp_access_groups && formValues.allowed_mcp_access_groups.length > 0) {
- if (!formValues.object_permission) {
- formValues.object_permission = {};
- }
- formValues.object_permission.mcp_access_groups = formValues.allowed_mcp_access_groups;
- delete formValues.allowed_mcp_access_groups;
- }
-
- // Add model_aliases if any are defined
- if (Object.keys(modelAliases).length > 0) {
- formValues.model_aliases = modelAliases;
- }
-
- const response: any = await teamCreateCall(accessToken, formValues);
- queryClient.invalidateQueries({ queryKey: organizationKeys.all });
- if (teams !== null) {
- setTeams([...teams, response]);
- } else {
- setTeams([response]);
- }
- console.log(`response for team create call: ${response}`);
- NotificationsManager.success("Team created");
- form.resetFields();
- setLoggingSettings([]);
- setModelAliases({});
- setIsTeamModalVisible(false);
- }
- } catch (error) {
- console.error("Error creating the team:", error);
- NotificationsManager.fromBackend("Error creating the team: " + error);
- }
- };
-
- return (
-
-
-
-
-
- Organization{" "}
-
- Organizations can have multiple teams. Learn more about{" "}
- e.stopPropagation()}
- >
- user management hierarchy
-
-
- }
- >
-
-
-
- }
- name="organization_id"
- initialValue={currentOrg ? currentOrg.organization_id : null}
- className="mt-8"
- >
- {
- form.setFieldValue("organization_id", value);
- setCurrentOrgForCreateTeam(organizations?.find((org) => org.organization_id === value) || null);
- }}
- filterOption={(input, option) => {
- if (!option) return false;
- const optionValue = option.children?.toString() || "";
- return optionValue.toLowerCase().includes(input.toLowerCase());
- }}
- optionFilterProp="children"
- >
- {organizations?.map((org) => (
-
- {org.organization_alias}{" "}
- ({org.organization_id})
-
- ))}
-
-
-
- Models{" "}
-
-
-
-
- }
- name="models"
- >
-
-
- All Proxy Models
-
- {modelsToPick.map((model) => (
-
- {getModelDisplayName(model)}
-
- ))}
-
-
-
-
-
- Team Member Settings
-
-
-
- Optional defaults applied when members join this team. All fields can be overridden per member.
-
- prev.models !== cur.models}
- >
- {({ getFieldValue }) => {
- const teamModels: string[] = getFieldValue("models") || [];
- const opts = teamModels.length > 0 ? teamModels : modelsToPick;
- return (
-
- Default Model Access{" "}
-
-
-
-
- }
- name="default_team_member_models"
- >
-
- {opts.map((m) => (
-
- {getModelDisplayName(m)}
-
- ))}
-
-
- );
- }}
-
- (value ? Number(value) : undefined)}
- tooltip="Default spend budget for each member in this team."
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- daily
- weekly
- monthly
-
-
-
-
-
-
-
-
-
- {
- if (!mcpAccessGroupsLoaded) {
- fetchMcpAccessGroups();
- setMcpAccessGroupsLoaded(true);
- }
- }}
- >
-
- Additional Settings
-
-
-
- {
- e.target.value = e.target.value.trim();
- }}
- />
-
-
-
-
- {
- if (!value) {
- return Promise.resolve();
- }
- try {
- JSON.parse(value);
- return Promise.resolve();
- } catch (error) {
- return Promise.reject(new Error("Please enter valid JSON"));
- }
- },
- },
- ]}
- >
-
-
-
- Guardrails{" "}
-
- e.stopPropagation()}
- >
-
-
-
-
- }
- name="guardrails"
- className="mt-8"
- help="Select existing guardrails or enter new ones"
- >
- ({
- value: name,
- label: name,
- }))}
- />
-
-
- Disable Global Guardrails{" "}
-
-
-
-
- }
- name="disable_global_guardrails"
- className="mt-4"
- valuePropName="checked"
- help="Bypass global guardrails for this team"
- >
-
-
-
- Policies{" "}
-
- e.stopPropagation()}
- >
-
-
-
-
- }
- name="policies"
- className="mt-8"
- help="Select existing policies or enter new ones"
- >
- ({
- value: name,
- label: name,
- }))}
- />
-
-
- Allowed Vector Stores{" "}
-
-
-
-
- }
- name="allowed_vector_store_ids"
- className="mt-8"
- help="Select vector stores this team can access. Leave empty for access to all vector stores"
- >
- form.setFieldValue("allowed_vector_store_ids", values)}
- value={form.getFieldValue("allowed_vector_store_ids")}
- accessToken={accessToken || ""}
- placeholder="Select vector stores (optional)"
- />
-
-
-
-
-
-
- MCP Settings
-
-
-
- Allowed MCP Servers{" "}
-
-
-
-
- }
- name="allowed_mcp_servers_and_groups"
- className="mt-4"
- help="Select MCP servers or access groups this team can access"
- >
- form.setFieldValue("allowed_mcp_servers_and_groups", val)}
- value={form.getFieldValue("allowed_mcp_servers_and_groups")}
- accessToken={accessToken || ""}
- placeholder="Select MCP servers or access groups (optional)"
- />
-
-
- {/* Hidden field to register mcp_tool_permissions with the form */}
-
-
-
-
-
- prevValues.allowed_mcp_servers_and_groups !== currentValues.allowed_mcp_servers_and_groups ||
- prevValues.mcp_tool_permissions !== currentValues.mcp_tool_permissions
- }
- >
- {() => (
-
- form.setFieldsValue({ mcp_tool_permissions: toolPerms })}
- />
-
- )}
-
-
-
-
-
-
- Agent Settings
-
-
-
- Allowed Agents{" "}
-
-
-
-
- }
- name="allowed_agents_and_groups"
- className="mt-4"
- help="Select agents or access groups this team can access"
- >
- form.setFieldValue("allowed_agents_and_groups", val)}
- value={form.getFieldValue("allowed_agents_and_groups")}
- accessToken={accessToken || ""}
- placeholder="Select agents or access groups (optional)"
- />
-
-
-
-
-
-
- Search Tool Settings
-
-
-
- Allowed Search Tools{" "}
-
-
-
-
- }
- name="object_permission_search_tools"
- className="mt-4"
- help="Restrict which configured search tools keys on this team may call."
- >
- form.setFieldValue("object_permission_search_tools", vals)}
- value={form.getFieldValue("object_permission_search_tools")}
- accessToken={accessToken || ""}
- placeholder="Select search tools (optional, empty = all allowed)"
- />
-
-
-
-
-
-
- Logging Settings
-
-
-
-
-
-
-
-
- Model Aliases
-
-
-
-
- Create custom aliases for models that can be used by team members in API calls. This allows you to
- create shortcuts for specific models.
-
-
-
-
-
- >
-
- Create Team
-
-
-
- );
-};
-
-export default CreateTeamModal;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/DeleteTeamModal.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/DeleteTeamModal.test.tsx
deleted file mode 100644
index 1e4907dcca..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/DeleteTeamModal.test.tsx
+++ /dev/null
@@ -1,171 +0,0 @@
-import { render, screen } from "@testing-library/react";
-import userEvent from "@testing-library/user-event";
-import React from "react";
-import { describe, expect, it, vi } from "vitest";
-import { Team } from "@/components/key_team_helpers/key_list";
-import DeleteTeamModal from "./DeleteTeamModal";
-
-const makeTeam = (overrides: Partial = {}): Team => ({
- team_id: "team-1",
- team_alias: "Engineering",
- models: [],
- max_budget: null,
- budget_duration: null,
- tpm_limit: null,
- rpm_limit: null,
- organization_id: "org-1",
- created_at: "2024-01-01T00:00:00Z",
- keys: [],
- members_with_roles: [],
- spend: 0,
- ...overrides,
-});
-
-const renderModal = (props: Partial[0]> = {}) => {
- const defaults = {
- teams: [makeTeam()],
- teamToDelete: "team-1",
- onCancel: vi.fn(),
- onConfirm: vi.fn(),
- };
- return render();
-};
-
-describe("DeleteTeamModal", () => {
- it("should render the title, team name label, and confirmation input", () => {
- renderModal();
-
- expect(screen.getByText("Delete Team")).toBeInTheDocument();
- expect(screen.getByText("Engineering")).toBeInTheDocument();
- expect(screen.getByPlaceholderText("Enter team name exactly")).toBeInTheDocument();
- });
-
- it("should render Cancel and Force Delete buttons", () => {
- renderModal();
-
- expect(screen.getByRole("button", { name: /^cancel$/i })).toBeInTheDocument();
- expect(screen.getByRole("button", { name: /force delete/i })).toBeInTheDocument();
- });
-
- it("should not show the warning banner when the team has no keys", () => {
- renderModal({ teams: [makeTeam({ keys: [] })] });
-
- expect(screen.queryByText(/Warning/i)).not.toBeInTheDocument();
- });
-
- it("should show a warning with singular 'key' when the team has exactly 1 key", () => {
- const team = makeTeam({ keys: [{ token: "tok-1" } as any] });
- renderModal({ teams: [team] });
-
- expect(screen.getByText(/This team has 1 associated key\./)).toBeInTheDocument();
- });
-
- it("should show a warning with plural 'keys' when the team has multiple keys", () => {
- const team = makeTeam({
- keys: [{ token: "tok-1" } as any, { token: "tok-2" } as any, { token: "tok-3" } as any],
- });
- renderModal({ teams: [team] });
-
- expect(screen.getByText(/This team has 3 associated keys\./)).toBeInTheDocument();
- });
-
- it("should note that associated keys will also be deleted in the warning", () => {
- const team = makeTeam({ keys: [{ token: "tok-1" } as any] });
- renderModal({ teams: [team] });
-
- expect(screen.getByText(/Deleting the team will also delete all associated keys/)).toBeInTheDocument();
- });
-
- it("should disable Force Delete when the input is empty", () => {
- renderModal();
-
- expect(screen.getByRole("button", { name: /force delete/i })).toBeDisabled();
- });
-
- it("should keep Force Delete disabled when the input does not exactly match the team name", async () => {
- const user = userEvent.setup();
- renderModal();
-
- await user.type(screen.getByPlaceholderText("Enter team name exactly"), "engineer");
-
- expect(screen.getByRole("button", { name: /force delete/i })).toBeDisabled();
- });
-
- it("should enable Force Delete only after typing the exact team name (case-sensitive)", async () => {
- const user = userEvent.setup();
- renderModal();
-
- const input = screen.getByPlaceholderText("Enter team name exactly");
-
- await user.type(input, "Engineering");
-
- expect(screen.getByRole("button", { name: /force delete/i })).toBeEnabled();
- });
-
- it("should call onConfirm when Force Delete is clicked with a valid input", async () => {
- const user = userEvent.setup();
- const onConfirm = vi.fn();
- renderModal({ onConfirm });
-
- await user.type(screen.getByPlaceholderText("Enter team name exactly"), "Engineering");
- await user.click(screen.getByRole("button", { name: /force delete/i }));
-
- expect(onConfirm).toHaveBeenCalledTimes(1);
- });
-
- it("should not call onConfirm when Force Delete is clicked with an invalid input", async () => {
- const user = userEvent.setup();
- const onConfirm = vi.fn();
- renderModal({ onConfirm });
-
- // Button is disabled so click has no effect
- await user.click(screen.getByRole("button", { name: /force delete/i }));
-
- expect(onConfirm).not.toHaveBeenCalled();
- });
-
- it("should call onCancel when the Cancel button is clicked", async () => {
- const user = userEvent.setup();
- const onCancel = vi.fn();
- renderModal({ onCancel });
-
- await user.click(screen.getByRole("button", { name: /^cancel$/i }));
-
- expect(onCancel).toHaveBeenCalledTimes(1);
- });
-
- it("should call onCancel when the Close button is clicked", async () => {
- const user = userEvent.setup();
- const onCancel = vi.fn();
- renderModal({ onCancel });
-
- await user.click(screen.getByRole("button", { name: /^close$/i }));
-
- expect(onCancel).toHaveBeenCalledTimes(1);
- });
-
- it("should reset the confirmation input when Cancel is clicked", async () => {
- const user = userEvent.setup();
- renderModal();
-
- const input = screen.getByPlaceholderText("Enter team name exactly");
- await user.type(input, "Engineering");
- expect(input).toHaveValue("Engineering");
-
- await user.click(screen.getByRole("button", { name: /^cancel$/i }));
-
- expect(input).toHaveValue("");
- });
-
- it("should reset the confirmation input when the Close button is clicked", async () => {
- const user = userEvent.setup();
- renderModal();
-
- const input = screen.getByPlaceholderText("Enter team name exactly");
- await user.type(input, "Engineering");
-
- await user.click(screen.getByRole("button", { name: /^close$/i }));
-
- expect(input).toHaveValue("");
- });
-});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/DeleteTeamModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/DeleteTeamModal.tsx
deleted file mode 100644
index 28d80faacd..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/DeleteTeamModal.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { AlertTriangleIcon, XIcon } from "lucide-react";
-import React, { useState } from "react";
-import { Team } from "@/components/key_team_helpers/key_list";
-
-interface DeleteTeamModalProps {
- teams: Team[] | null;
- teamToDelete: string | null;
- onCancel: () => void;
- onConfirm: () => void;
-}
-
-const DeleteTeamModal = ({ teams, teamToDelete, onCancel, onConfirm }: DeleteTeamModalProps) => {
- const [deleteConfirmInput, setDeleteConfirmInput] = useState("");
-
- const team = teams?.find((t) => t.team_id === teamToDelete);
- const teamName = team?.team_alias || "";
- const keyCount = team?.keys?.length || 0;
- const isValid = deleteConfirmInput === teamName;
-
- return (
-
-
-
-
- Delete Team
-
-
-
- {keyCount > 0 && (
-
-
-
-
- Warning: This team has {keyCount} associated key{keyCount > 1 ? "s" : ""}.
-
-
- Deleting the team will also delete all associated keys. This action is irreversible.
-
-
-
- )}
-
- Are you sure you want to force delete this team and all its keys?
-
-
-
- setDeleteConfirmInput(e.target.value)}
- placeholder="Enter team name exactly"
- className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-base"
- autoFocus
- />
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default DeleteTeamModal;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/hooks/useFetchTeams.ts b/ui/litellm-dashboard/src/app/(dashboard)/teams/hooks/useFetchTeams.ts
deleted file mode 100644
index c02787896f..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/hooks/useFetchTeams.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { useCallback, useEffect, useState } from "react";
-import { fetchTeams } from "@/components/common_components/fetch_teams";
-import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-import { Organization, Team } from "@/components/networking";
-
-interface useFetchTeamsProps {
- currentOrg: Organization | null;
- setTeams: (teams: Team[] | null) => void;
-}
-
-const useFetchTeams = ({ currentOrg, setTeams }: useFetchTeamsProps) => {
- const [lastRefreshed, setLastRefreshed] = useState("");
- const { accessToken, userId, userRole } = useAuthorized();
-
- const onRefreshClick = useCallback(() => {
- const currentDate = new Date();
- setLastRefreshed(currentDate.toLocaleString());
- }, []);
-
- useEffect(() => {
- if (accessToken) {
- fetchTeams(accessToken, userId, userRole, currentOrg, setTeams).then();
- }
- onRefreshClick();
- }, [accessToken, currentOrg, lastRefreshed, onRefreshClick, setTeams, userId, userRole]);
-
- return { lastRefreshed, setLastRefreshed, onRefreshClick };
-};
-
-export default useFetchTeams;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/page.tsx
deleted file mode 100644
index 041c50dd32..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/teams/page.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-"use client";
-
-import TeamsView from "@/app/(dashboard)/teams/TeamsView";
-import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-import useTeams from "@/app/(dashboard)/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 [organizations, setOrganizations] = useState([]);
-
- useEffect(() => {
- fetchOrganizations(accessToken, setOrganizations).then(() => {});
- }, [accessToken]);
-
- return (
-
- );
-};
-
-export default TeamsPage;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/test-key/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/test-key/page.tsx
deleted file mode 100644
index 0f984686c4..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/test-key/page.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-"use client";
-
-import ChatUI from "@/components/playground/chat_ui/ChatUI";
-import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-import { useState, useEffect } from "react";
-import { fetchProxySettings } from "@/utils/proxyUtils";
-
-interface ProxySettings {
- PROXY_BASE_URL?: string;
- LITELLM_UI_API_DOC_BASE_URL?: string | null;
-}
-
-const TestKeyPage = () => {
- const { token, accessToken, userRole, userId, disabledPersonalKeyCreation } = useAuthorized();
- const [proxySettings, setProxySettings] = useState(undefined);
-
- useEffect(() => {
- const initializeProxySettings = async () => {
- if (accessToken) {
- const settings = await fetchProxySettings(accessToken);
- if (settings) {
- setProxySettings({
- PROXY_BASE_URL: settings.PROXY_BASE_URL || undefined,
- LITELLM_UI_API_DOC_BASE_URL: settings.LITELLM_UI_API_DOC_BASE_URL,
- });
- }
- }
- };
-
- initializeProxySettings();
- }, [accessToken]);
-
- return (
-
- );
-};
-
-export default TestKeyPage;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/tools/mcp-servers/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/tools/mcp-servers/page.tsx
deleted file mode 100644
index 9b94de6c9f..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/tools/mcp-servers/page.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-"use client";
-
-import { MCPServers } from "@/components/mcp_tools";
-import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-
-const MCPServersPage = () => {
- const { accessToken, userRole, userId } = useAuthorized();
-
- return ;
-};
-
-export default MCPServersPage;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/tools/vector-stores/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/tools/vector-stores/page.tsx
deleted file mode 100644
index 8516a0faa1..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/tools/vector-stores/page.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-"use client";
-
-import VectorStoreManagement from "@/components/vector_store_management";
-import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-
-const VectorStoresPage = () => {
- const { accessToken, userId, userRole } = useAuthorized();
-
- return ;
-};
-
-export default VectorStoresPage;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/page.tsx
deleted file mode 100644
index 477c1163ce..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/usage/page.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-"use client";
-
-import UsagePageView from "@/components/UsagePage/components/UsagePageView";
-import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-import useTeams from "@/app/(dashboard)/hooks/useTeams";
-
-const UsagePage = () => {
- const { accessToken, userRole, userId, premiumUser } = useAuthorized();
- const { teams } = useTeams();
-
- return ;
-};
-
-export default UsagePage;
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/page.tsx
deleted file mode 100644
index 9874dd4886..0000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/users/page.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-"use client";
-
-import ViewUserDashboard from "@/components/view_users";
-import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-import useTeams from "@/app/(dashboard)/hooks/useTeams";
-import { useOrganizations } from "@/app/(dashboard)/hooks/organizations/useOrganizations";
-import { isProxyAdminRole } from "@/utils/roles";
-import { useState, useMemo } from "react";
-import { Organization } from "@/components/networking";
-
-const UsersPage = () => {
- const { accessToken, userRole, userId, token } = useAuthorized();
- const [keys, setKeys] = useState([]);
-
- const { teams } = useTeams();
- const { data: organizations, isLoading: isOrgsLoading } = useOrganizations();
-
- // Three states:
- // - undefined: org data still loading (non-proxy-admin) — query should wait
- // - null: proxy admin or no org filtering needed — query runs unfiltered
- // - Array<{organization_id, organization_alias}>: org admin orgs — query runs filtered
- const orgAdminOrgIds = useMemo((): Array<{organization_id: string, organization_alias: string}> | null | undefined => {
- if (!userId || !userRole) return null;
- // Proxy admins see all users — no org filtering
- if (isProxyAdminRole(userRole)) return null;
-
- // Still loading org data — signal "not ready yet"
- if (isOrgsLoading || !organizations) return undefined;
-
- const adminOrgs = organizations
- .filter((org: Organization) =>
- org.members?.some((member) => member.user_id === userId && member.user_role === "org_admin")
- )
- .map((org: Organization) => ({ organization_id: org.organization_id, organization_alias: org.organization_alias }));
-
- return adminOrgs.length > 0 ? adminOrgs : null;
- }, [userId, organizations, userRole, isOrgsLoading]);
-
- return (
-
- );
-};
-
-export default UsersPage;
|