"use client"; import React, { useEffect, useState } from "react"; import { keyDeleteCall, getTotalSpendCall } from "./networking"; import { StatusOnlineIcon, TrashIcon } from "@heroicons/react/outline"; import { Accordion, AccordionHeader, AccordionList, DonutChart } from "@tremor/react"; import { Badge, Card, Table, Metric, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text, Title, Icon, AccordionBody, List, ListItem, } from "@tremor/react"; import { Statistic } from "antd" import { spendUsersCall, modelAvailableCall } from "./networking"; const isLocal = process.env.NODE_ENV === "development"; const proxyBaseUrl = isLocal ? "http://localhost:4000" : null; if (isLocal != true) { console.log = function() {}; } // Define the props type interface UserSpendData { spend: number; // Adjust the type accordingly based on your data max_budget?: number | null; // Optional property with a default of null // Add other properties if needed } interface ViewUserSpendProps { userID: string | null; userRole: string | null; accessToken: string | null; userSpend: number | null; userMaxBudget: number | null; selectedTeam: any | null; } const ViewUserSpend: React.FC<ViewUserSpendProps> = ({ userID, userRole, accessToken, userSpend, userMaxBudget, selectedTeam }) => { console.log(`userSpend: ${userSpend}`) let [spend, setSpend] = useState(userSpend !== null ? userSpend : 0.0); const [maxBudget, setMaxBudget] = useState(selectedTeam ? selectedTeam.max_budget : null); useEffect(() => { if (selectedTeam) { if (selectedTeam.team_alias === "Default Team") { setMaxBudget(userMaxBudget); } else { let setMaxBudgetFlag = false; if (selectedTeam.team_memberships) { /** * What 'team_memberships' looks like: * "team_memberships": [ * { * "user_id": "2c315de3-e7ce-4269-b73e-b039a06187b1", * "team_id": "test-team_515e6f42-ded2-4f0d-8919-0a1f43c5a45f", * "budget_id": "0880769f-716a-4149-ab19-7f7651ad4db5", * "litellm_budget_table": { "soft_budget": null, "max_budget": 20.0, "max_parallel_requests": null, "tpm_limit": null, "rpm_limit": null, "model_max_budget": null, "budget_duration": null } */ for (const member of selectedTeam.team_memberships) { if (member.user_id === userID && "max_budget" in member.litellm_budget_table && member.litellm_budget_table.max_budget !== null) { setMaxBudget(member.litellm_budget_table.max_budget); setMaxBudgetFlag = true; } } } if (!setMaxBudgetFlag) { setMaxBudget(selectedTeam.max_budget); } } } }, [selectedTeam, userMaxBudget]); const [userModels, setUserModels] = useState([]); useEffect(() => { const fetchData = async () => { if (!accessToken || !userID || !userRole) { return; } }; const fetchUserModels = async () => { try { if (userID === null || userRole === null) { return; } if (accessToken !== null) { const model_available = await modelAvailableCall(accessToken, userID, userRole); let available_model_names = model_available["data"].map( (element: { id: string }) => element.id ); console.log("available_model_names:", available_model_names); setUserModels(available_model_names); } } catch (error) { console.error("Error fetching user models:", error); } }; fetchUserModels(); fetchData(); }, [userRole, accessToken, userID]); useEffect(() => { if (userSpend !== null) { setSpend(userSpend) } }, [userSpend]) // logic to decide what models to display let modelsToDisplay = []; if (selectedTeam && selectedTeam.models) { modelsToDisplay = selectedTeam.models; } // check if "all-proxy-models" is in modelsToDisplay if (modelsToDisplay && modelsToDisplay.includes("all-proxy-models")) { console.log("user models:", userModels); modelsToDisplay = userModels; } else if (modelsToDisplay && modelsToDisplay.includes("all-team-models")) { modelsToDisplay = selectedTeam.models; } else if (modelsToDisplay && modelsToDisplay.length === 0) { modelsToDisplay = userModels; } const displayMaxBudget = maxBudget !== null ? `$${maxBudget} limit` : "No limit"; const roundedSpend = spend !== undefined ? spend.toFixed(4) : null; console.log(`spend in view user spend: ${spend}`) return ( <div className="flex items-center"> <div className="flex justify-between gap-x-6"> <div> <p className="text-tremor-default text-tremor-content dark:text-dark-tremor-content"> Total Spend </p> <p className="text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold"> ${roundedSpend} </p> </div> <div> <p className="text-tremor-default text-tremor-content dark:text-dark-tremor-content"> Max Budget </p> <p className="text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold"> {displayMaxBudget} </p> </div> </div> {/* <div className="ml-auto"> <Accordion> <AccordionHeader><Text>Team Models</Text></AccordionHeader> <AccordionBody className="absolute right-0 z-10 bg-white p-2 shadow-lg max-w-xs"> <List> {modelsToDisplay.map((model: string) => ( <ListItem key={model}> <Text>{model}</Text> </ListItem> ))} </List> </AccordionBody> </Accordion> </div> */} </div> ); } export default ViewUserSpend;