Merge pull request #19192 from BerriAI/litellm_ui_team_member_table

[Refactor] UI - Team Member Icon Buttons
This commit is contained in:
yuneng-jiang 2026-01-16 09:00:57 -08:00 committed by GitHub
commit 416ad7ea35
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 44 additions and 19 deletions

View file

@ -20,6 +20,7 @@ vi.mock("@/utils/roles", () => ({
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { isProxyAdminRole, isUserTeamAdminForSingleTeam } from "@/utils/roles";
describe("TeamMembersComponent", () => {
const mockHandleMemberDelete = vi.fn();
@ -162,4 +163,31 @@ describe("TeamMembersComponent", () => {
expect(screen.getByText("Add Member")).toBeInTheDocument();
});
it("should show delete button for proxy admin when canEditTeam is true", () => {
vi.mocked(isProxyAdminRole).mockReturnValue(true);
vi.mocked(isUserTeamAdminForSingleTeam).mockReturnValue(false);
const { container } = renderWithProviders(
<TeamMembersComponent
teamData={mockTeamData}
canEditTeam={true}
handleMemberDelete={mockHandleMemberDelete}
setSelectedEditMember={mockSetSelectedEditMember}
setIsEditMemberModalVisible={mockSetIsEditMemberModalVisible}
setIsAddMemberModalVisible={mockSetIsAddMemberModalVisible}
/>,
);
// Verify that action buttons are rendered when canEditTeam is true
// For proxy admin, both edit and delete buttons should be visible
// Check for clickable icon elements (Tremor Icon components with cursor-pointer class)
const clickableIcons = container.querySelectorAll('[class*="cursor-pointer"]');
// Should have at least 4 icons: 2 edit buttons + 2 delete buttons for 2 members
expect(clickableIcons.length).toBeGreaterThanOrEqual(4);
// Verify members are rendered
expect(screen.getAllByText("user1@test.com").length).toBeGreaterThan(0);
expect(screen.getAllByText("user2@test.com").length).toBeGreaterThan(0);
});
});

View file

@ -1,25 +1,24 @@
import React from "react";
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { Member } from "@/components/networking";
import { formatNumberWithCommas } from "@/utils/dataUtils";
import { isProxyAdminRole, isUserTeamAdminForSingleTeam } from "@/utils/roles";
import { InfoCircleOutlined } from "@ant-design/icons";
import {
Card,
Table,
TableHead,
TableRow,
TableHeaderCell,
TableBody,
TableCell,
TableHead,
TableHeaderCell,
TableRow,
Text,
Icon,
Button as TremorButton,
} from "@tremor/react";
import { InfoCircleOutlined } from "@ant-design/icons";
import { Tooltip } from "antd";
import React from "react";
import TableIconActionButton from "../common_components/IconActionButton/TableIconActionButtons/TableIconActionButton";
import { TeamData } from "./team_info";
import { PencilAltIcon, TrashIcon } from "@heroicons/react/outline";
import { formatNumberWithCommas } from "@/utils/dataUtils";
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
import { isUserTeamAdminForSingleTeam, isProxyAdminRole } from "@/utils/roles";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
interface TeamMembersComponentProps {
teamData: TeamData;
@ -154,9 +153,9 @@ const TeamMembersComponent: React.FC<TeamMembersComponentProps> = ({
<TableCell className="sticky right-0 bg-white z-10 border-l border-gray-200">
{canEditTeam && (
<div className="flex gap-2">
<Icon
icon={PencilAltIcon}
size="sm"
<TableIconActionButton
variant="Edit"
tooltipText="Edit member"
onClick={() => {
// Get budget and rate limit data from team membership
const membership = teamData.team_memberships.find((tm) => tm.user_id === member.user_id);
@ -169,14 +168,12 @@ const TeamMembersComponent: React.FC<TeamMembersComponentProps> = ({
setSelectedEditMember(enhancedMember);
setIsEditMemberModalVisible(true);
}}
className="cursor-pointer hover:text-blue-600"
/>
{(isProxyAdmin || (isUserTeamAdmin && !disableTeamAdminDeleteTeamUser)) && (
<Icon
icon={TrashIcon}
size="sm"
<TableIconActionButton
variant="Delete"
tooltipText="Delete member"
onClick={() => handleMemberDelete(member)}
className="cursor-pointer hover:text-red-600"
/>
)}
</div>