forked from phoenix/litellm-mirror
ui - edit user flow
This commit is contained in:
parent
098df3dcd6
commit
dd81a7ee9b
3 changed files with 185 additions and 30 deletions
119
ui/litellm-dashboard/src/components/edit_user.tsx
Normal file
119
ui/litellm-dashboard/src/components/edit_user.tsx
Normal file
|
@ -0,0 +1,119 @@
|
|||
import { useState } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogPanel,
|
||||
TextInput,
|
||||
Button,
|
||||
Select,
|
||||
SelectItem,
|
||||
Text,
|
||||
} 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();
|
||||
|
||||
const handleChange = (e) => {
|
||||
setEditedUser({ ...editedUser, [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
onSubmit(editedUser);
|
||||
onCancel();
|
||||
};
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<Modal visible={visible} onCancel={onCancel}>
|
||||
|
||||
<Text>
|
||||
{JSON.stringify(user)}
|
||||
</Text>
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={handleSubmit}
|
||||
initialValues={user} // Pass initial values here
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
labelAlign="left"
|
||||
>
|
||||
<>
|
||||
<Form.Item
|
||||
className="mt-8"
|
||||
label="user_email"
|
||||
name="user_email">
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="user_id"
|
||||
name="user_id"
|
||||
tooltip="int (optional) - Tokens limit for this deployment: in tokens per minute (tpm). Find this information on your model/providers website"
|
||||
>
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="user_role"
|
||||
name="user_role"
|
||||
tooltip="int (optional) - Tokens limit for this deployment: in tokens per minute (tpm). Find this information on your model/providers website"
|
||||
>
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="spend"
|
||||
name="spend"
|
||||
tooltip="int (optional) - Tokens limit for this deployment: in tokens per minute (tpm). Find this information on your model/providers website"
|
||||
>
|
||||
<InputNumber min={0} step={1} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="max_budget"
|
||||
name="max_budget"
|
||||
tooltip="int (optional) - Tokens limit for this deployment: in tokens per minute (tpm). Find this information on your model/providers website"
|
||||
>
|
||||
<InputNumber min={0} step={1} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="rpm"
|
||||
name="rpm"
|
||||
tooltip="int (optional) - Rate limit for this deployment: in requests per minute (rpm). Find this information on your model/providers website"
|
||||
>
|
||||
<InputNumber min={0} step={1} />
|
||||
</Form.Item>
|
||||
<Button onClick={handleSubmit}>Save</Button>
|
||||
</>
|
||||
|
||||
</Form>
|
||||
|
||||
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditUserModal;
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -28,8 +28,14 @@ import { userInfoCall } 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,32 @@ 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 handleEditClick = (user) => {
|
||||
console.log("handleEditClick:", user);
|
||||
setSelectedUser(user);
|
||||
setEditModalVisible(true);
|
||||
};
|
||||
|
||||
const handleEditCancel = () => {
|
||||
setEditModalVisible(false);
|
||||
setSelectedUser(null);
|
||||
};
|
||||
|
||||
const handleEditSubmit = async (editedUser) => {
|
||||
// Call your API to update the user with editedUser data
|
||||
// ...
|
||||
|
||||
// Update the userData state with the updated user data
|
||||
const updatedUserData = userData.map((user) =>
|
||||
user.user_id === editedUser.user_id ? editedUser : user
|
||||
);
|
||||
setUserData(updatedUserData);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!accessToken || !token || !userRole || !userID) {
|
||||
return;
|
||||
|
@ -146,7 +176,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 +204,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
|
||||
|
||||
}
|
||||
Keys
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge size={"xs"} color={"gray"}>
|
||||
|
@ -188,12 +223,28 @@ 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= {() => {
|
||||
handleEditClick(user)
|
||||
}}>View Keys</Icon>
|
||||
|
||||
<Icon icon={TrashIcon} onClick= {() => {
|
||||
setOpenDialogId(user.user_id)
|
||||
setSelectedItem(user)
|
||||
}}>View Keys</Icon>
|
||||
|
||||
</TableCell>
|
||||
|
||||
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
|
@ -226,30 +277,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>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue