import React, { useState, useEffect } from "react"; import NumericalInput from "../shared/numerical_input"; import { Card, Title, Text, Tab, TabList, TabGroup, TabPanel, TabPanels, Grid, Badge, Button as TremorButton, TableRow, TableCell, TableHead, TableHeaderCell, TableBody, Table, Icon } from "@tremor/react"; import TeamMembersComponent from "./team_member_view"; import MemberPermissions from "./member_permissions"; import { teamInfoCall, teamMemberDeleteCall, teamMemberAddCall, teamMemberUpdateCall, Member, teamUpdateCall } from "@/components/networking"; import { Button, Form, Input, Select, message, Tooltip } from "antd"; import { InfoCircleOutlined } from '@ant-design/icons'; import { Select as Select2, } from "antd"; import { PencilAltIcon, PlusIcon, TrashIcon } from "@heroicons/react/outline"; import MemberModal from "./edit_membership"; import UserSearchModal from "@/components/common_components/user_search_modal"; import { getModelDisplayName } from "../key_team_helpers/fetch_available_models_team_key"; import { isAdminRole } from "@/utils/roles"; import ObjectPermissionsView from "../object_permissions_view"; import VectorStoreSelector from "../vector_store_management/VectorStoreSelector"; import MCPServerSelector from "../mcp_server_management/MCPServerSelector"; import PremiumVectorStoreSelector from "../common_components/PremiumVectorStoreSelector"; export interface TeamData { team_id: string; team_info: { team_alias: string; team_id: string; organization_id: string | null; admins: string[]; members: string[]; members_with_roles: Member[]; metadata: Record; tpm_limit: number | null; rpm_limit: number | null; max_budget: number | null; budget_duration: string | null; models: string[]; blocked: boolean; spend: number; max_parallel_requests: number | null; budget_reset_at: string | null; model_id: string | null; litellm_model_table: { model_aliases: Record; } | null; created_at: string; object_permission?: { object_permission_id: string; mcp_servers: string[]; vector_stores: string[]; }; }; keys: any[]; team_memberships: any[]; } export interface TeamInfoProps { teamId: string; onUpdate: (data: any) => void; onClose: () => void; accessToken: string | null; is_team_admin: boolean; is_proxy_admin: boolean; userModels: string[]; editTeam: boolean; premiumUser?: boolean; } const TeamInfoView: React.FC = ({ teamId, onClose, accessToken, is_team_admin, is_proxy_admin, userModels, editTeam, premiumUser = false }) => { const [teamData, setTeamData] = useState(null); const [loading, setLoading] = useState(true); const [isAddMemberModalVisible, setIsAddMemberModalVisible] = useState(false); const [form] = Form.useForm(); const [isEditMemberModalVisible, setIsEditMemberModalVisible] = useState(false); const [selectedEditMember, setSelectedEditMember] = useState(null); const [isEditing, setIsEditing] = useState(false); console.log("userModels in team info", userModels); const canEditTeam = is_team_admin || is_proxy_admin; const fetchTeamInfo = async () => { try { setLoading(true); if (!accessToken) return; const response = await teamInfoCall(accessToken, teamId); setTeamData(response); } catch (error) { message.error("Failed to load team information"); console.error("Error fetching team info:", error); } finally { setLoading(false); } }; useEffect(() => { fetchTeamInfo(); }, [teamId, accessToken]); const handleMemberCreate = async (values: any) => { try { if (accessToken == null) return; const member: Member = { user_email: values.user_email, user_id: values.user_id, role: values.role, }; await teamMemberAddCall(accessToken, teamId, member); message.success("Team member added successfully"); setIsAddMemberModalVisible(false); form.resetFields(); fetchTeamInfo(); } catch (error: any) { let errMsg = "Failed to add team member"; if (error?.raw?.detail?.error?.includes("Assigning team admins is a premium feature")) { errMsg = "Assigning admins is an enterprise-only feature. Please upgrade your LiteLLM plan to enable this."; } else if (error?.message) { errMsg = error.message; } message.error(errMsg); console.error("Error adding team member:", error); } }; const handleMemberUpdate = async (values: any) => { try { if (accessToken == null) { return; } const member: Member = { user_email: values.user_email, user_id: values.user_id, role: values.role, } message.destroy(); // Remove all existing toasts await teamMemberUpdateCall(accessToken, teamId, member); message.success("Team member updated successfully"); setIsEditMemberModalVisible(false); fetchTeamInfo(); } catch (error: any) { let errMsg = "Failed to update team member"; if (error?.raw?.detail?.includes("Assigning team admins is a premium feature")) { errMsg = "Assigning admins is an enterprise-only feature. Please upgrade your LiteLLM plan to enable this."; } else if (error?.message) { errMsg = error.message; } setIsEditMemberModalVisible(false); message.destroy(); // Remove all existing toasts message.error(errMsg); console.error("Error updating team member:", error); } }; const handleMemberDelete = async (member: Member) => { try { if (accessToken == null) { return; } const response = await teamMemberDeleteCall(accessToken, teamId, member); message.success("Team member removed successfully"); fetchTeamInfo(); } catch (error) { message.error("Failed to remove team member"); console.error("Error removing team member:", error); } }; const handleTeamUpdate = async (values: any) => { try { if (!accessToken) return; let parsedMetadata = {}; try { parsedMetadata = values.metadata ? JSON.parse(values.metadata) : {}; } catch (e) { message.error("Invalid JSON in metadata field"); return; } const updateData: any = { team_id: teamId, team_alias: values.team_alias, models: values.models, tpm_limit: values.tpm_limit, rpm_limit: values.rpm_limit, max_budget: values.max_budget, budget_duration: values.budget_duration, metadata: { ...parsedMetadata, guardrails: values.guardrails || [] }, organization_id: values.organization_id, }; // Handle object_permission updates if (values.vector_stores !== undefined || values.mcp_servers !== undefined) { updateData.object_permission = { ...teamData?.team_info.object_permission, vector_stores: values.vector_stores || [], mcp_servers: values.mcp_servers || [] }; } const response = await teamUpdateCall(accessToken, updateData); message.success("Team settings updated successfully"); setIsEditing(false); fetchTeamInfo(); } catch (error) { console.error("Error updating team:", error); } }; if (loading) { return
Loading...
; } if (!teamData?.team_info) { return
Team not found
; } const { team_info: info } = teamData; return (
{info.team_alias} {info.team_id}
{[ Overview, ...(canEditTeam ? [ Members, Member Permissions, Settings ] : []) ]} {/* Overview Panel */} Budget Status
${info.spend.toFixed(6)} of {info.max_budget === null ? "Unlimited" : `$${info.max_budget}`} {info.budget_duration && ( Reset: {info.budget_duration} )}
Rate Limits
TPM: {info.tpm_limit || 'Unlimited'} RPM: {info.rpm_limit || 'Unlimited'} {info.max_parallel_requests && ( Max Parallel Requests: {info.max_parallel_requests} )}
Models
{info.models.length === 0 ? ( All proxy models ) : ( info.models.map((model, index) => ( {model} )) )}
{/* Members Panel */} {/* Member Permissions Panel */} {canEditTeam && ( )} {/* Settings Panel */}
Team Settings {(canEditTeam && !isEditing) && ( setIsEditing(true)} > Edit Settings )}
{isEditing ? (
Guardrails{' '} e.stopPropagation()} > } name="guardrails" help="Select existing guardrails or enter new ones" >
Save Changes
) : (
Team Name
{info.team_alias}
Team ID
{info.team_id}
Created At
{new Date(info.created_at).toLocaleString()}
Models
{info.models.map((model, index) => ( {model} ))}
Rate Limits
TPM: {info.tpm_limit || 'Unlimited'}
RPM: {info.rpm_limit || 'Unlimited'}
Budget
Max: {info.max_budget !== null ? `$${info.max_budget}` : 'No Limit'}
Reset: {info.budget_duration || 'Never'}
Organization ID
{info.organization_id}
Status {info.blocked ? 'Blocked' : 'Active'}
)}
setIsEditMemberModalVisible(false)} onSubmit={handleMemberUpdate} initialData={selectedEditMember} mode="edit" config={{ title: "Edit Member", showEmail: true, showUserId: true, roleOptions: [ { label: "Admin", value: "admin" }, { label: "User", value: "user" } ] }} /> setIsAddMemberModalVisible(false)} onSubmit={handleMemberCreate} accessToken={accessToken} />
); }; export default TeamInfoView;