forked from phoenix/litellm-mirror
(ui) show user table on admin UI
This commit is contained in:
parent
6546b43e5c
commit
3a662539bd
4 changed files with 116 additions and 5 deletions
|
@ -4,6 +4,7 @@ import { useSearchParams } from "next/navigation";
|
||||||
import Navbar from "../components/navbar";
|
import Navbar from "../components/navbar";
|
||||||
import UserDashboard from "../components/user_dashboard";
|
import UserDashboard from "../components/user_dashboard";
|
||||||
import ModelDashboard from "@/components/model_dashboard";
|
import ModelDashboard from "@/components/model_dashboard";
|
||||||
|
import ViewUserDashboard from "@/components/view_users";
|
||||||
import ChatUI from "@/components/chat_ui";
|
import ChatUI from "@/components/chat_ui";
|
||||||
import Sidebar from "../components/leftnav";
|
import Sidebar from "../components/leftnav";
|
||||||
import Usage from "../components/usage";
|
import Usage from "../components/usage";
|
||||||
|
@ -68,12 +69,13 @@ const CreateKeyPage = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<div>Loading...</div>}>
|
<Suspense fallback={<div>Loading...</div>}>
|
||||||
<div className="flex flex-col min-h-screen">
|
<div className="flex flex-col min-h-screen">
|
||||||
<Navbar userID={userID} userRole={userRole} userEmail={userEmail} />
|
<Navbar userID={userID} userRole={userRole} userEmail={userEmail} />
|
||||||
<div className="flex flex-1 overflow-auto">
|
<div className="flex flex-1 overflow-auto">
|
||||||
<Sidebar setPage={setPage} />
|
<Sidebar setPage={setPage} userRole={userRole}/>
|
||||||
{page == "api-keys" ? (
|
{page == "api-keys" ? (
|
||||||
<UserDashboard
|
<UserDashboard
|
||||||
userID={userID}
|
userID={userID}
|
||||||
|
@ -97,6 +99,14 @@ const CreateKeyPage = () => {
|
||||||
accessToken={accessToken}
|
accessToken={accessToken}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
: page == "users" ? (
|
||||||
|
<ViewUserDashboard
|
||||||
|
userID={userID}
|
||||||
|
userRole={userRole}
|
||||||
|
token={token}
|
||||||
|
accessToken={accessToken}
|
||||||
|
/>
|
||||||
|
)
|
||||||
: (
|
: (
|
||||||
<Usage
|
<Usage
|
||||||
userID={userID}
|
userID={userID}
|
||||||
|
|
|
@ -6,9 +6,10 @@ const { Sider } = Layout;
|
||||||
// Define the props type
|
// Define the props type
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
setPage: React.Dispatch<React.SetStateAction<string>>;
|
setPage: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
userRole: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Sidebar: React.FC<SidebarProps> = ({ setPage }) => {
|
const Sidebar: React.FC<SidebarProps> = ({ setPage, userRole }) => {
|
||||||
return (
|
return (
|
||||||
<Layout style={{ minHeight: "100vh", maxWidth: "120px" }}>
|
<Layout style={{ minHeight: "100vh", maxWidth: "120px" }}>
|
||||||
<Sider width={120}>
|
<Sider width={120}>
|
||||||
|
@ -29,6 +30,13 @@ const Sidebar: React.FC<SidebarProps> = ({ setPage }) => {
|
||||||
<Menu.Item key="4" onClick={() => setPage("usage")}>
|
<Menu.Item key="4" onClick={() => setPage("usage")}>
|
||||||
Usage
|
Usage
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
{
|
||||||
|
userRole == "Admin" ?
|
||||||
|
<Menu.Item key="5" onClick={() => setPage("users")}>
|
||||||
|
Users
|
||||||
|
</Menu.Item>
|
||||||
|
: null
|
||||||
|
}
|
||||||
</Menu>
|
</Menu>
|
||||||
</Sider>
|
</Sider>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -104,14 +104,19 @@ export const keyDeleteCall = async (accessToken: String, user_key: String) => {
|
||||||
|
|
||||||
export const userInfoCall = async (
|
export const userInfoCall = async (
|
||||||
accessToken: String,
|
accessToken: String,
|
||||||
userID: String,
|
userID: String | null,
|
||||||
userRole: String
|
userRole: String,
|
||||||
|
viewAll: Boolean = false
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
let url = proxyBaseUrl ? `${proxyBaseUrl}/user/info` : `/user/info`;
|
let url = proxyBaseUrl ? `${proxyBaseUrl}/user/info` : `/user/info`;
|
||||||
if (userRole == "App Owner") {
|
if (userRole == "App Owner" && userID) {
|
||||||
url = `${url}/?user_id=${userID}`;
|
url = `${url}/?user_id=${userID}`;
|
||||||
}
|
}
|
||||||
|
console.log("in userInfoCall viewAll=", viewAll);
|
||||||
|
if (viewAll) {
|
||||||
|
url = `${url}/?view_all=true`;
|
||||||
|
}
|
||||||
message.info("Requesting user data");
|
message.info("Requesting user data");
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|
88
ui/litellm-dashboard/src/components/view_users.tsx
Normal file
88
ui/litellm-dashboard/src/components/view_users.tsx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { Card, Title, Subtitle, Table, TableHead, TableRow, TableCell, TableBody, Metric, Grid } from "@tremor/react";
|
||||||
|
import { userInfoCall } from "./networking";
|
||||||
|
import { Badge, BadgeDelta, Button } from '@tremor/react';
|
||||||
|
import RequestAccess from "./request_model_access";
|
||||||
|
|
||||||
|
interface ViewUserDashboardProps {
|
||||||
|
accessToken: string | null;
|
||||||
|
token: string | null;
|
||||||
|
userRole: string | null;
|
||||||
|
userID: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
|
||||||
|
accessToken,
|
||||||
|
token,
|
||||||
|
userRole,
|
||||||
|
userID,
|
||||||
|
}) => {
|
||||||
|
const [userData, setuserData] = useState<null | any[]>(null);
|
||||||
|
const [pendingRequests, setPendingRequests] = useState<any[]>([]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accessToken || !token || !userRole || !userID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fetchData = async () => {
|
||||||
|
try {
|
||||||
|
// Replace with your actual API call for model data
|
||||||
|
const userDataResponse = await userInfoCall(accessToken, null, userRole, true);
|
||||||
|
console.log("user data response:", userDataResponse);
|
||||||
|
setuserData(userDataResponse);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("There was an error fetching the model data", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (accessToken && token && userRole && userID) {
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
}, [accessToken, token, userRole, userID]);
|
||||||
|
|
||||||
|
if (!userData) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accessToken || !token || !userRole || !userID) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when users click request access show pop up to allow them to request access
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ width: "100%" }}>
|
||||||
|
<Grid className="gap-2 p-10 h-[75vh] w-full">
|
||||||
|
<Card>
|
||||||
|
<Table className="mt-5">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell><Title>User ID </Title></TableCell>
|
||||||
|
<TableCell><Title>User Role</Title></TableCell>
|
||||||
|
<TableCell><Title>User Models</Title></TableCell>
|
||||||
|
<TableCell><Title>User Spend ($ USD)</Title></TableCell>
|
||||||
|
<TableCell><Title>User Max Budget ($ USD)</Title></TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{userData.map((user: any) => (
|
||||||
|
<TableRow key={user.user_id}>
|
||||||
|
<TableCell><Title>{user.user_id}</Title></TableCell>
|
||||||
|
<TableCell><Title>{user.user_role ? user.user_role : "app_user"}</Title></TableCell>
|
||||||
|
<TableCell><Title>{user.models && user.models.length > 0 ? user.models : "All Models"}</Title></TableCell>
|
||||||
|
<TableCell><Title>{user.spend ? user.spend : 0}</Title></TableCell>
|
||||||
|
<TableCell><Title>{user.max_budget ? user.max_budget : "Unlimited"}</Title></TableCell>
|
||||||
|
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ViewUserDashboard;
|
Loading…
Add table
Add a link
Reference in a new issue