Merge pull request #3904 from BerriAI/litellm_ui_edit_users

[Admin UI] Edit `Internal Users`
This commit is contained in:
Ishaan Jaff 2024-05-29 17:49:36 -07:00 committed by GitHub
commit 40e7560d6b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 207 additions and 31 deletions

View file

@ -0,0 +1,136 @@
import { useEffect, useState } from 'react';
import {
Dialog,
DialogPanel,
TextInput,
Button,
Select,
SelectItem,
Text,
Title,
Subtitle,
} from '@tremor/react';
import {
Button as Button2,
Modal,
Form,
Input,
Select as Select2,
InputNumber,
message,
} from "antd";
interface EditUserModalProps {
visible: boolean;
onCancel: () => void;
user: any;
onSubmit: (data: any) => void;
}
const EditUserModal: React.FC<EditUserModalProps> = ({ visible, onCancel, user, onSubmit }) => {
const [editedUser, setEditedUser] = useState(user);
const [form] = Form.useForm();
useEffect(() => {
form.resetFields();
}, [user]);
const handleChange = (e) => {
setEditedUser({ ...editedUser, [e.target.name]: e.target.value });
};
const handleCancel = async () => {
form.resetFields();
onCancel();
};
const handleEditSubmit = async (formValues: Record<string, any>) => {
// Call API to update team with teamId and values
form.resetFields();
onSubmit(formValues);
onCancel();
};
if (!user) {
return null;
}
return (
<Modal
visible={visible}
onCancel={handleCancel}
footer={null}
title={"Edit User " + user.user_id}
width={1000}
>
<Form
form={form}
onFinish={handleEditSubmit}
initialValues={user} // Pass initial values here
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
labelAlign="left"
>
<>
<Form.Item
className="mt-8"
label="User Email"
tooltip="Email of the User"
name="user_email">
<TextInput />
</Form.Item>
<Form.Item
label="user_id"
name="user_id"
hidden={true}
>
<TextInput />
</Form.Item>
<Form.Item
label="User Role"
name="user_role"
>
<Select2>
<Select2.Option value="proxy_admin">Proxy Admin (Can create, edit, delete keys, teams)</Select2.Option>
<Select2.Option value="proxy_admin_viewer">Proxy Viewer (Can just view spend, cannot created keys, teams)</Select2.Option>
</Select2>
</Form.Item>
<Form.Item
label="Spend (USD)"
name="spend"
tooltip="(float) - Spend of all LLM calls completed by this user"
>
<InputNumber min={0} step={1} />
</Form.Item>
<Form.Item
label="User Budget (USD)"
name="max_budget"
tooltip="(float) - Maximum budget of this user"
>
<InputNumber min={0} step={1} />
</Form.Item>
<div style={{ textAlign: "right", marginTop: "10px" }}>
<Button2 htmlType="submit">Save</Button2>
</div>
</>
</Form>
</Modal>
);
};
export default EditUserModal;

View file

@ -79,7 +79,7 @@ const Sidebar: React.FC<SidebarProps> = ({
{userRole == "Admin" ? (
<Menu.Item key="5" onClick={() => setPage("users")}>
<Text>Users</Text>
<Text>Internal Users</Text>
</Menu.Item>
) : null}

View file

@ -24,12 +24,18 @@ import {
Icon,
TextInput,
} from "@tremor/react";
import { userInfoCall } from "./networking";
import { userInfoCall, userUpdateUserCall } from "./networking";
import { Badge, BadgeDelta, Button } from "@tremor/react";
import RequestAccess from "./request_model_access";
import CreateUser from "./create_user_button";
import EditUserModal from "./edit_user";
import Paragraph from "antd/es/skeleton/Paragraph";
import InformationCircleIcon from "@heroicons/react/outline/InformationCircleIcon";
import {
PencilAltIcon,
InformationCircleIcon,
TrashIcon,
} from "@heroicons/react/outline";
interface ViewUserDashboardProps {
accessToken: string | null;
@ -55,8 +61,35 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
const [currentPage, setCurrentPage] = useState(0);
const [openDialogId, setOpenDialogId] = React.useState<null | number>(null);
const [selectedItem, setSelectedItem] = useState<null | any>(null);
const [editModalVisible, setEditModalVisible] = useState(false);
const [selectedUser, setSelectedUser] = useState(null);
const defaultPageSize = 25;
const handleEditCancel = async () => {
setSelectedUser(null);
setEditModalVisible(false);
};
const handleEditSubmit = async (editedUser: any) => {
console.log("inside handleEditSubmit:", editedUser);
if (!accessToken || !token || !userRole || !userID) {
return;
}
userUpdateUserCall(accessToken, editedUser, userRole);
if (userData) {
const updatedUserData = userData.map((user) =>
user.user_id === editedUser.user_id ? editedUser : user
);
setUserData(updatedUserData);
}
setSelectedUser(null);
setEditModalVisible(false);
// Close the modal
};
useEffect(() => {
if (!accessToken || !token || !userRole || !userID) {
return;
@ -146,7 +179,8 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
<TableHeaderCell>User Models</TableHeaderCell>
<TableHeaderCell>User Spend ($ USD)</TableHeaderCell>
<TableHeaderCell>User Max Budget ($ USD)</TableHeaderCell>
<TableHeaderCell>User API Key Aliases</TableHeaderCell>
<TableHeaderCell>API Keys</TableHeaderCell>
<TableHeaderCell></TableHeaderCell>
</TableRow>
</TableHead>
<TableBody>
@ -173,9 +207,13 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
(key: any) => key !== null
).length > 0 ? (
<Badge size={"xs"} color={"indigo"}>
{user.key_aliases
.filter((key: any) => key !== null)
.join(", ")}
{
user.key_aliases.filter(
(key: any) => key !== null
).length
}
&nbsp;Keys
</Badge>
) : (
<Badge size={"xs"} color={"gray"}>
@ -188,12 +226,29 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
</Badge>
)}
{/* <Text>{user.key_aliases.filter(key => key !== null).length} Keys</Text> */}
{/* <Icon icon={InformationCircleIcon} onClick= {() => {
setOpenDialogId(user.user_id)
setSelectedItem(user)
}}>View Keys</Icon> */}
</Grid>
</TableCell>
<TableCell>
<Icon icon={InformationCircleIcon} onClick= {() => {
setOpenDialogId(user.user_id)
setSelectedItem(user)
}}>View Keys</Icon>
<Icon icon={PencilAltIcon} onClick= {() => {
setSelectedUser(user)
setEditModalVisible(true)
}}>View Keys</Icon>
<Icon icon={TrashIcon} onClick= {() => {
setOpenDialogId(user.user_id)
setSelectedItem(user)
}}>View Keys</Icon>
</TableCell>
</TableRow>
))}
</TableBody>
@ -226,30 +281,15 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
</TabPanel>
</TabPanels>
</TabGroup>
<EditUserModal
visible={editModalVisible}
onCancel={handleEditCancel}
user={selectedUser}
onSubmit={handleEditSubmit}
/>
</Card>
{renderPagination()}
</Grid>
{/* <Dialog
open={openDialogId !== null}
onClose={() => {
setOpenDialogId(null);
}}
>
<DialogPanel>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
<Title>Key Aliases</Title>
<Text>
{selectedItem && selectedItem.key_aliases
? selectedItem.key_aliases.filter(key => key !== null).length > 0
? selectedItem.key_aliases.filter(key => key !== null).join(', ')
: 'No Keys'
: "No Keys"}
</Text>
</div>
</DialogPanel>
</Dialog> */}
</div>
);
};