forked from phoenix/litellm-mirror
Merge branch 'main' into litellm_daily_metrics
This commit is contained in:
commit
990439c49c
33 changed files with 366 additions and 380 deletions
|
@ -15,6 +15,7 @@ const CreateKeyPage = () => {
|
|||
const [userRole, setUserRole] = useState("");
|
||||
const [userEmail, setUserEmail] = useState<null | string>(null);
|
||||
const [teams, setTeams] = useState<null | any[]>(null);
|
||||
const [showSSOBanner, setShowSSOBanner] = useState<boolean>(true);
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const userID = searchParams.get("userID");
|
||||
|
@ -48,6 +49,14 @@ const CreateKeyPage = () => {
|
|||
} else {
|
||||
console.log(`User Email is not set ${decoded}`);
|
||||
}
|
||||
|
||||
if (decoded.login_method) {
|
||||
setShowSSOBanner(
|
||||
decoded.login_method == "username_password" ? true : false
|
||||
);
|
||||
} else {
|
||||
console.log(`User Email is not set ${decoded}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [token]);
|
||||
|
@ -74,7 +83,12 @@ const CreateKeyPage = () => {
|
|||
return (
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Navbar userID={userID} userRole={userRole} userEmail={userEmail} />
|
||||
<Navbar
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
userEmail={userEmail}
|
||||
showSSOBanner={showSSOBanner}
|
||||
/>
|
||||
<div className="flex flex-1 overflow-auto">
|
||||
<Sidebar
|
||||
setPage={setPage}
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
"use client";
|
||||
import React, { Suspense, useEffect, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import Navbar from "../../components/navbar";
|
||||
import Sidebar from "../../components/leftnav";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
Card,
|
||||
Icon,
|
||||
Button,
|
||||
Col,
|
||||
Grid,
|
||||
} from "@tremor/react";
|
||||
import { CogIcon } from "@heroicons/react/outline";
|
||||
|
||||
const TeamSettingsPage = () => {
|
||||
const [userRole, setUserRole] = useState("");
|
||||
const [userEmail, setUserEmail] = useState<null | string>(null);
|
||||
const [teams, setTeams] = useState<null | string[]>(null);
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const userID = searchParams.get("userID");
|
||||
const token = searchParams.get("token");
|
||||
|
||||
const [page, setPage] = useState("team");
|
||||
const [accessToken, setAccessToken] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
const decoded = jwtDecode(token) as { [key: string]: any };
|
||||
if (decoded) {
|
||||
// cast decoded to dictionary
|
||||
console.log("Decoded token:", decoded);
|
||||
|
||||
console.log("Decoded key:", decoded.key);
|
||||
// set accessToken
|
||||
setAccessToken(decoded.key);
|
||||
|
||||
// check if userRole is defined
|
||||
if (decoded.user_role) {
|
||||
const formattedUserRole = formatUserRole(decoded.user_role);
|
||||
console.log("Decoded user_role:", formattedUserRole);
|
||||
setUserRole(formattedUserRole);
|
||||
} else {
|
||||
console.log("User role not defined");
|
||||
}
|
||||
|
||||
if (decoded.user_email) {
|
||||
setUserEmail(decoded.user_email);
|
||||
} else {
|
||||
console.log(`User Email is not set ${decoded}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
function formatUserRole(userRole: string) {
|
||||
if (!userRole) {
|
||||
return "Undefined Role";
|
||||
}
|
||||
console.log(`Received user role: ${userRole}`);
|
||||
switch (userRole.toLowerCase()) {
|
||||
case "app_owner":
|
||||
return "App Owner";
|
||||
case "demo_app_owner":
|
||||
return "App Owner";
|
||||
case "app_admin":
|
||||
return "Admin";
|
||||
case "app_user":
|
||||
return "App User";
|
||||
default:
|
||||
return "Unknown Role";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Navbar userID={userID} userRole={userRole} userEmail={userEmail} />
|
||||
<div className="flex flex-1 overflow-auto">
|
||||
<Grid numItems={1} className="gap-0 p-10 h-[75vh] w-full">
|
||||
<Col numColSpan={1}>
|
||||
<Card className="w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>Team Name</TableHeaderCell>
|
||||
<TableHeaderCell>Spend (USD)</TableHeaderCell>
|
||||
<TableHeaderCell>Budget (USD)</TableHeaderCell>
|
||||
<TableHeaderCell>TPM / RPM Limits</TableHeaderCell>
|
||||
<TableHeaderCell>Settings</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell>Wilhelm Tell</TableCell>
|
||||
<TableCell className="text-right">1</TableCell>
|
||||
<TableCell>Uri, Schwyz, Unterwalden</TableCell>
|
||||
<TableCell>National Hero</TableCell>
|
||||
<TableCell>
|
||||
<Icon icon={CogIcon} size="sm" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>The Witcher</TableCell>
|
||||
<TableCell className="text-right">129</TableCell>
|
||||
<TableCell>Kaedwen</TableCell>
|
||||
<TableCell>Legend</TableCell>
|
||||
<TableCell>
|
||||
<Icon icon={CogIcon} size="sm" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>Mizutsune</TableCell>
|
||||
<TableCell className="text-right">82</TableCell>
|
||||
<TableCell>Japan</TableCell>
|
||||
<TableCell>N/A</TableCell>
|
||||
<TableCell>
|
||||
<Icon icon={CogIcon} size="sm" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col numColSpan={1}>
|
||||
<Link
|
||||
href={`/team?userID=${searchParams.get(
|
||||
"userID"
|
||||
)}&token=${searchParams.get("token")}`}
|
||||
>
|
||||
<Button className="mx-auto">+ Create New Team</Button>
|
||||
</Link>
|
||||
</Col>
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
export default TeamSettingsPage;
|
|
@ -20,8 +20,14 @@ interface NavbarProps {
|
|||
userID: string | null;
|
||||
userRole: string | null;
|
||||
userEmail: string | null;
|
||||
showSSOBanner: boolean;
|
||||
}
|
||||
const Navbar: React.FC<NavbarProps> = ({ userID, userRole, userEmail }) => {
|
||||
const Navbar: React.FC<NavbarProps> = ({
|
||||
userID,
|
||||
userRole,
|
||||
userEmail,
|
||||
showSSOBanner,
|
||||
}) => {
|
||||
console.log("User ID:", userID);
|
||||
console.log("userEmail:", userEmail);
|
||||
|
||||
|
@ -46,8 +52,20 @@ const Navbar: React.FC<NavbarProps> = ({ userID, userRole, userEmail }) => {
|
|||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right mx-4 my-2 absolute top-0 right-0">
|
||||
<Button variant="secondary">
|
||||
<div className="text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2">
|
||||
{showSSOBanner ? (
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/proxy/ui#setup-ssoauth-for-ui"
|
||||
target="_blank"
|
||||
className="mr-2"
|
||||
>
|
||||
<Button variant="primary" size="lg">
|
||||
Enable SSO
|
||||
</Button>
|
||||
</a>
|
||||
) : null}
|
||||
|
||||
<Button variant="secondary" size="lg">
|
||||
{userEmail}
|
||||
<p>Role: {userRole}</p>
|
||||
<p>ID: {userID}</p>
|
||||
|
|
|
@ -95,7 +95,7 @@ const Team: React.FC<TeamProps> = ({
|
|||
const user_role: Member = {
|
||||
role: "user",
|
||||
user_email: formValues.user_email,
|
||||
user_id: null,
|
||||
user_id: formValues.user_id,
|
||||
};
|
||||
const response: any = await teamMemberAddCall(
|
||||
accessToken,
|
||||
|
@ -313,8 +313,18 @@ const Team: React.FC<TeamProps> = ({
|
|||
labelAlign="left"
|
||||
>
|
||||
<>
|
||||
<Form.Item label="Email" name="user_email">
|
||||
<Input />
|
||||
<Form.Item label="Email" name="user_email" className="mb-4">
|
||||
<Input
|
||||
name="user_email"
|
||||
className="px-3 py-2 border rounded-md w-full"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="text-center mb-4">OR</div>
|
||||
<Form.Item label="User ID" name="user_id" className="mb-4">
|
||||
<Input
|
||||
name="user_id"
|
||||
className="px-3 py-2 border rounded-md w-full"
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
<div style={{ textAlign: "right", marginTop: "10px" }}>
|
||||
|
|
|
@ -6,9 +6,6 @@ import CreateKey from "./create_key_button";
|
|||
import ViewKeyTable from "./view_key_table";
|
||||
import ViewUserSpend from "./view_user_spend";
|
||||
import DashboardTeam from "./dashboard_default_team";
|
||||
import EnterProxyUrl from "./enter_proxy_url";
|
||||
import { message } from "antd";
|
||||
import Navbar from "./navbar";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
|
||||
|
@ -83,6 +80,7 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
|
|||
}
|
||||
}
|
||||
|
||||
// console.log(`selectedTeam: ${Object.entries(selectedTeam)}`);
|
||||
// Moved useEffect inside the component and used a condition to run fetch only if the params are available
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
|
@ -127,6 +125,7 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
|
|||
setUserSpendData(response["user_info"]);
|
||||
setData(response["keys"]); // Assuming this is the correct path to your data
|
||||
setTeams(response["teams"]);
|
||||
setSelectedTeam(response["teams"] ? response["teams"][0] : null);
|
||||
sessionStorage.setItem(
|
||||
"userData" + userID,
|
||||
JSON.stringify(response["keys"])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue