From 3a0e1cefde29786493ddb3693f7ad0d831656996 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 12 Aug 2026 13:57:10 -0700 Subject: [PATCH] refactor(ui): migrate team detail controls --- ui/litellm-dashboard/eslint-suppressions.json | 6 - .../src/components/team/TeamMemberTab.tsx | 58 ++++---- .../components/team/TeamVirtualKeysTable.tsx | 136 +++++++++--------- 3 files changed, 93 insertions(+), 107 deletions(-) diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 8a86305b4cb..c6f600d90ec 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -3525,17 +3525,11 @@ "src/components/team/TeamMemberTab.tsx": { "local/no-complex-jsx-arrow": { "count": 1 - }, - "no-restricted-imports": { - "count": 2 } }, "src/components/team/TeamVirtualKeysTable.tsx": { "no-nested-ternary": { "count": 1 - }, - "no-restricted-imports": { - "count": 2 } }, "src/components/team/member_permissions.tsx": { diff --git a/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx b/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx index 4e04063197c..ddc43ac50f1 100644 --- a/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamMemberTab.tsx @@ -1,13 +1,13 @@ import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; +import { Tooltip } from "@/components/atoms/Tooltip"; +import MemberTable from "@/components/common_components/MemberTable"; import { Member } from "@/components/networking"; import { DateCell, MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas } from "@/utils/dataUtils"; import { isProxyAdminRole, isUserTeamAdminForSingleTeam } from "@/utils/roles"; -import { InfoCircleOutlined } from "@ant-design/icons"; -import { Space, Tooltip, Typography } from "antd"; -import type { ColumnsType } from "antd/es/table"; -import MemberTable from "@/components/common_components/MemberTable"; +import { CircleHelp } from "lucide-react"; +import type { ComponentProps } from "react"; import { TeamData } from "./TeamInfo"; interface TeamMemberTabProps { @@ -97,48 +97,48 @@ export default function TeamMemberTab({ return membership?.litellm_budget_table?.budget_reset_at ?? null; }; - const extraColumns: ColumnsType = [ + const extraColumns: NonNullable["extraColumns"]> = [ { title: ( - + Model Scope - - + + - + ), key: "model_scope", render: (_: unknown, record: Member) => { const models = getUserAllowedModels(record.user_id); if (!models) { - return (all team models); + return (all team models); } const displayed = models.slice(0, 2); const remaining = models.length - displayed.length; return ( - +
{displayed.map((m) => ( - + {m} - + ))} {remaining > 0 && ( - - +{remaining} more + + +{remaining} more )} - +
); }, }, { title: ( - + Current Cycle Spend (USD) - - + + - + ), key: "spend", render: (_: unknown, record: Member) => ( @@ -147,12 +147,12 @@ export default function TeamMemberTab({ }, { title: ( - + Total Spend (USD) - - + + - + ), key: "total_spend", render: (_: unknown, record: Member) => , @@ -171,15 +171,15 @@ export default function TeamMemberTab({ }, { title: ( - + Team Member Rate Limits - - + + - + ), key: "rate_limits", - render: (_: unknown, record: Member) => {getUserRateLimits(record.user_id)}, + render: (_: unknown, record: Member) => {getUserRateLimits(record.user_id)}, }, ]; diff --git a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx index 66690e5478f..f4097b082f8 100644 --- a/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamVirtualKeysTable.tsx @@ -1,5 +1,7 @@ "use client"; import { useKeys } from "@/app/(dashboard)/hooks/keys/useKeys"; +import { Tooltip } from "@/components/atoms/Tooltip"; +import CopyButton from "@/components/shared/CopyButton"; import { DateCell, IdCell, MoneyCell } from "@/components/shared/table_cells"; import { DataTable, @@ -8,13 +10,13 @@ import { DataTableSortHeader, DataTableToolbar, } from "@/components/shared/DataTable"; +import { Badge } from "@/components/ui/badge"; +import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"; import { Input } from "@/components/ui/input"; import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants"; -import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline"; import { useDebouncedValue } from "@tanstack/react-pacer/debouncer"; import { ColumnDef, ColumnFiltersState, OnChangeFn, PaginationState, SortingState } from "@tanstack/react-table"; -import { Badge, Icon, Text } from "@tremor/react"; -import { Popover, Tooltip, Typography } from "antd"; +import { ChevronDown, ChevronRight } from "lucide-react"; import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { getModelDisplayName } from "../key_team_helpers/fetch_available_models_team_key"; @@ -147,12 +149,9 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi enableSorting: true, cell: (info) => { const value = info.getValue() as string; - const width = info.cell.column.getSize(); return ( - - - {value ?? "-"} - + + {value ?? "-"} ); }, @@ -182,12 +181,9 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi cell: (info) => { const user = info.getValue() as { user_email?: string } | undefined; const value = user?.user_email; - const width = info.cell.column.getSize(); return ( - - - {value ?? "-"} - + + {value ?? "-"} ); }, @@ -201,12 +197,9 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi cell: (info) => { const userId = info.getValue() as string | null; const displayValue = userId === "default_user_id" ? "Default Proxy Admin" : userId; - const width = info.cell.column.getSize(); return ( - - - {displayValue ?? "-"} - + + {displayValue ?? "-"} ); }, @@ -234,21 +227,21 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi const userEmail = created_by_user?.user_email ?? null; const isDefaultAdmin = userId === "default_user_id"; const displayValue = userAlias || userEmail || userId; - const width = info.cell.column.getSize(); const popoverContent = ( -
+
{[ { label: "User Alias", value: userAlias }, { label: "User Email", value: userEmail }, { label: "User ID", value: userId }, ].map(({ label, value }) => (
- {label} + {label} {value ? ( - - {value} - + + {value} + + ) : ( - )} @@ -259,23 +252,24 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi if (isDefaultAdmin && !userAlias && !userEmail) { return ( - - + + }> - - + + {popoverContent} + ); } return ( - - + } > {displayValue} - - + + {popoverContent} + ); }, }, @@ -342,14 +336,14 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi const models = info.getValue() as string[]; const scope = deriveKeyModelScope(info.row.original.allowed_routes, info.row.original.key_type); const emptyModelsBadge = !scope.hasModelAccess ? ( - - - No model access + + + No model access ) : ( - - All Proxy Models + + All Proxy Models ); return ( @@ -362,57 +356,55 @@ export function TeamVirtualKeysTable({ teamId, teamAlias, organization }: TeamVi <>
{models.length > 3 && ( -
- - setExpandedAccordions((prev) => ({ - ...prev, - [info.row.id]: !prev[info.row.id], - })) - } - /> -
+ )}
{models.slice(0, 3).map((model, index) => model === "all-proxy-models" ? ( - - All Proxy Models + + All Proxy Models ) : ( - - - {model.length > 30 - ? `${getModelDisplayName(model).slice(0, 30)}...` - : getModelDisplayName(model)} - + + {model.length > 30 + ? `${getModelDisplayName(model).slice(0, 30)}...` + : getModelDisplayName(model)} ), )} {models.length > 3 && !expandedAccordions[info.row.id] && ( - - - +{models.length - 3} {models.length - 3 === 1 ? "more model" : "more models"} - + + +{models.length - 3} {models.length - 3 === 1 ? "more model" : "more models"} )} {expandedAccordions[info.row.id] && (
{models.slice(3).map((model, index) => model === "all-proxy-models" ? ( - - All Proxy Models + + All Proxy Models ) : ( - - - {model.length > 30 - ? `${getModelDisplayName(model).slice(0, 30)}...` - : getModelDisplayName(model)} - + + {model.length > 30 + ? `${getModelDisplayName(model).slice(0, 30)}...` + : getModelDisplayName(model)} ), )}