diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index e8d4d24a0f4..980385bbf56 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -52,6 +52,7 @@ "src/components/common_components/AutoRotationView.tsx", "src/components/agents/agent_card.tsx", "src/components/agents/agent_table.tsx", + "src/components/view_users/columns.tsx", "src/components/permissions/VectorStorePermissions.tsx", "src/components/permissions/AgentPermissions.tsx", "src/components/permissions/MCPServerPermissions.tsx", diff --git a/ui/litellm-dashboard/src/components/view_users/columns.tsx b/ui/litellm-dashboard/src/components/view_users/columns.tsx index 4b9fad8f23c..881d1f38e54 100644 --- a/ui/litellm-dashboard/src/components/view_users/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_users/columns.tsx @@ -1,9 +1,15 @@ import { ColumnDef } from "@tanstack/react-table"; -import { Badge, Grid, Icon } from "@tremor/react"; -import { Tooltip, Checkbox } from "antd"; +import { Badge } from "@/components/ui/badge"; +import { Checkbox } from "@/components/ui/checkbox"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; +import { Copy, Info, Pencil, RefreshCcw, Trash2 } from "lucide-react"; import { UserInfo } from "./types"; -import { PencilAltIcon, TrashIcon, InformationCircleIcon, RefreshIcon } from "@heroicons/react/outline"; -import { CopyOutlined } from "@ant-design/icons"; import { formatNumberWithCommas, copyToClipboard } from "@/utils/dataUtils"; interface SelectionOptions { @@ -15,6 +21,33 @@ interface SelectionOptions { isIndeterminate: boolean; } +const IconTipButton: React.FC<{ + tooltip: string; + onClick: () => void; + icon: React.ReactNode; + className?: string; + ariaLabel?: string; +}> = ({ tooltip, onClick, icon, className, ariaLabel }) => ( + + + + + + {tooltip} + + +); + export const columns = ( possibleUIRoles: Record>, handleEdit: (user: UserInfo) => void, @@ -23,7 +56,6 @@ export const columns = ( handleUserClick: (userId: string, openInEditMode?: boolean) => void, selectionOptions?: SelectionOptions, ): ColumnDef[] => { - // Backend sortable columns: user_id, user_email, created_at, spend, user_alias, user_role const baseColumns: ColumnDef[] = [ { header: "User ID", @@ -31,19 +63,30 @@ export const columns = ( enableSorting: true, cell: ({ row }) => (
- - {row.original.user_id ? `${row.original.user_id.slice(0, 7)}...` : "-"} - - {row.original.user_id && ( - - { - e.stopPropagation(); - copyToClipboard(row.original.user_id, "User ID copied to clipboard"); - }} - className="cursor-pointer text-gray-500 hover:text-blue-500 text-xs" - /> + + + + + {row.original.user_id + ? `${row.original.user_id.slice(0, 7)}...` + : "-"} + + + {row.original.user_id} + + {row.original.user_id && ( + + copyToClipboard( + row.original.user_id, + "User ID copied to clipboard", + ) + } + icon={} + className="hover:text-primary" + /> )}
), @@ -52,26 +95,38 @@ export const columns = ( header: "Email", accessorKey: "user_email", enableSorting: true, - cell: ({ row }) => {row.original.user_email || "-"}, + cell: ({ row }) => ( + {row.original.user_email || "-"} + ), }, { header: "Global Proxy Role", accessorKey: "user_role", enableSorting: true, - cell: ({ row }) => {possibleUIRoles?.[row.original.user_role]?.ui_label || "-"}, + cell: ({ row }) => ( + + {possibleUIRoles?.[row.original.user_role]?.ui_label || "-"} + + ), }, { header: "User Alias", accessorKey: "user_alias", enableSorting: false, - cell: ({ row }) => {row.original.user_alias || "-"}, + cell: ({ row }) => ( + {row.original.user_alias || "-"} + ), }, { header: "Spend (USD)", accessorKey: "spend", enableSorting: true, cell: ({ row }) => ( - {row.original.spend ? formatNumberWithCommas(row.original.spend, 4) : "-"} + + {row.original.spend + ? formatNumberWithCommas(row.original.spend, 4) + : "-"} + ), }, { @@ -79,22 +134,36 @@ export const columns = ( accessorKey: "max_budget", enableSorting: false, cell: ({ row }) => ( - {row.original.max_budget !== null ? row.original.max_budget : "Unlimited"} + + {row.original.max_budget !== null + ? row.original.max_budget + : "Unlimited"} + ), }, { header: () => (
SSO ID - - - + + + + + + + SSO ID is the ID of the user in the SSO provider. If the user + is not using SSO, this will be null. + + +
), accessorKey: "sso_user_id", enableSorting: false, cell: ({ row }) => ( - {row.original.sso_user_id !== null ? row.original.sso_user_id : "-"} + + {row.original.sso_user_id !== null ? row.original.sso_user_id : "-"} + ), }, { @@ -102,17 +171,18 @@ export const columns = ( accessorKey: "key_count", enableSorting: false, cell: ({ row }) => ( - +
{row.original.key_count > 0 ? ( - - {row.original.key_count} {row.original.key_count === 1 ? "Key" : "Keys"} + + {row.original.key_count}{" "} + {row.original.key_count === 1 ? "Key" : "Keys"} ) : ( - + No Keys )} - +
), }, { @@ -121,7 +191,9 @@ export const columns = ( enableSorting: true, cell: ({ row }) => ( - {row.original.created_at ? new Date(row.original.created_at).toLocaleDateString() : "-"} + {row.original.created_at + ? new Date(row.original.created_at).toLocaleDateString() + : "-"} ), }, @@ -131,7 +203,9 @@ export const columns = ( enableSorting: false, cell: ({ row }) => ( - {row.original.updated_at ? new Date(row.original.updated_at).toLocaleDateString() : "-"} + {row.original.updated_at + ? new Date(row.original.updated_at).toLocaleDateString() + : "-"} ), }, @@ -141,38 +215,37 @@ export const columns = ( enableSorting: false, cell: ({ row }) => (
- - handleUserClick(row.original.user_id, true)} - className="cursor-pointer hover:text-blue-600" - /> - - - handleDelete(row.original)} - className="cursor-pointer hover:text-red-600" - /> - - - handleResetPassword(row.original.user_id)} - className="cursor-pointer hover:text-green-600" - /> - + handleUserClick(row.original.user_id, true)} + icon={} + className="hover:text-primary" + /> + handleDelete(row.original)} + icon={} + className="hover:text-destructive" + /> + handleResetPassword(row.original.user_id)} + icon={} + className="hover:text-emerald-600 dark:hover:text-emerald-400" + />
), }, ]; - // Add selection column if selection is enabled if (selectionOptions) { - const { onSelectUser, onSelectAll, isUserSelected, isAllSelected, isIndeterminate } = selectionOptions; + const { + onSelectUser, + onSelectAll, + isUserSelected, + isAllSelected, + isIndeterminate, + } = selectionOptions; return [ { @@ -180,16 +253,17 @@ export const columns = ( enableSorting: false, header: () => ( onSelectAll(e.target.checked)} + checked={ + isIndeterminate ? "indeterminate" : isAllSelected ? true : false + } + onCheckedChange={(c) => onSelectAll(c === true)} onClick={(e) => e.stopPropagation()} /> ), cell: ({ row }) => ( onSelectUser(row.original, e.target.checked)} + onCheckedChange={(c) => onSelectUser(row.original, c === true)} onClick={(e) => e.stopPropagation()} /> ),