diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx index 29c9b17cb21..5a5fb475f08 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.test.tsx @@ -42,8 +42,16 @@ const MOCK_USER_DATA_NO_TEAMS = { teams: [], }; +vi.mock("next/navigation", () => ({ + useRouter: () => ({ push: vi.fn() }), + usePathname: () => "/users", + useSearchParams: () => new URLSearchParams(window.location.search), +})); + +// entityLinks -> migratedPages imports serverRootPath from the same module, so the mock must export it too. vi.mock("@/components/networking", () => { return { + serverRootPath: "/", userGetInfoV2: (...args: any[]) => mockUserGetInfoV2(...args), userDeleteCall: vi.fn(), userUpdateUserCall: (...args: unknown[]) => mockUserUpdateUserCall(...args), @@ -145,6 +153,21 @@ describe("UserInfoView", () => { }); }); + it("should link each team name to its team page", async () => { + render(); + + await waitFor(() => { + expect(screen.getByRole("link", { name: "Alpha Team" })).toHaveAttribute( + "href", + expect.stringContaining("/teams?team=team-1"), + ); + expect(screen.getByRole("link", { name: "Beta Team" })).toHaveAttribute( + "href", + expect.stringContaining("/teams?team=team-2"), + ); + }); + }); + it("should show 'No teams' when user has no teams", async () => { mockUserGetInfoV2.mockResolvedValue(MOCK_USER_DATA_NO_TEAMS); render(); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.tsx b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.tsx index 34b34066f14..853dc570796 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users/user_info_view.tsx @@ -34,6 +34,8 @@ import { } from "@/components/networking"; import { Button as AntdButton, Modal, Select as AntdSelect, Form, Tooltip } from "antd"; import { rolesWithWriteAccess } from "@/utils/roles"; +import { teamDetailHref } from "@/utils/entityLinks"; +import { BadgeLink } from "@/components/shared/BadgeLink"; import { UserEditView } from "../user_edit_view"; import OnboardingModal, { InvitationLink } from "@/components/onboarding_link"; import { formatNumberWithCommas, copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils"; @@ -484,7 +486,11 @@ export default function UserInfoView({ {teamDetails.slice(0, isTeamsExpanded ? teamDetails.length : 20).map((team) => ( - {team.team_alias || team.team_id} + + + {team.team_alias || team.team_id} + + {isProxyAdmin && (