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"; 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; }; 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; } const TeamInfoView: React.FC = ({ teamId, onClose, accessToken, is_team_admin, is_proxy_admin, userModels, editTeam }) => { 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, } const response = await teamMemberAddCall(accessToken, teamId, member); message.success("Team member added successfully"); setIsAddMemberModalVisible(false); form.resetFields(); fetchTeamInfo(); } catch (error) { message.error("Failed to add team member"); 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, } const response = await teamMemberUpdateCall(accessToken, teamId, member); message.success("Team member updated successfully"); setIsEditMemberModalVisible(false); fetchTeamInfo(); } catch (error) { message.error("Failed to update team member"); 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 = { 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 || [] } }; const response = await teamUpdateCall(accessToken, updateData); message.success("Team settings updated successfully"); setIsEditing(false); fetchTeamInfo(); } catch (error) { message.error("Failed to update team settings"); 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.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" >