Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions ui/litellm-dashboard/src/components/networking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,39 @@ export const keyDeleteCall = async (accessToken: String, user_key: String) => {
}
};

export const userDeleteCall = async (accessToken: string, userIds: string[]) => {
try {
const url = proxyBaseUrl ? `${proxyBaseUrl}/user/delete` : `/user/delete`;
console.log("in userDeleteCall:", userIds);

const response = await fetch(url, {
method: "POST",
headers: {
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
user_ids: userIds,
}),
});

if (!response.ok) {
const errorData = await response.text();
handleError(errorData);
throw new Error("Network response was not ok");
}

const data = await response.json();
console.log(data);
//message.success("User(s) Deleted");
return data;
} catch (error) {
console.error("Failed to delete user(s):", error);
throw error;
}
};


export const teamDeleteCall = async (accessToken: String, teamID: String) => {
try {
const url = proxyBaseUrl ? `${proxyBaseUrl}/team/delete` : `/team/delete`;
Expand Down
87 changes: 87 additions & 0 deletions ui/litellm-dashboard/src/components/view_users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from "@tremor/react";

import { message } from "antd";
import { Modal } from "antd";

import {
userInfoCall,
Expand All @@ -43,6 +44,8 @@ import {
TrashIcon,
} from "@heroicons/react/outline";

import { userDeleteCall } from "./networking";

interface ViewUserDashboardProps {
accessToken: string | null;
token: string | null;
Expand Down Expand Up @@ -84,11 +87,42 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
const [selectedItem, setSelectedItem] = useState<null | any>(null);
const [editModalVisible, setEditModalVisible] = useState(false);
const [selectedUser, setSelectedUser] = useState(null);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [userToDelete, setUserToDelete] = useState<string | null>(null);
const [possibleUIRoles, setPossibleUIRoles] = useState<
Record<string, Record<string, string>>
>({});
const defaultPageSize = 25;

const handleDelete = (userId: string) => {
setUserToDelete(userId);
setIsDeleteModalOpen(true);
};

const confirmDelete = async () => {
if (userToDelete && accessToken) {
try {
await userDeleteCall(accessToken, [userToDelete]);
message.success("User deleted successfully");
// Update the user list after deletion
if (userData) {
const updatedUserData = userData.filter(user => user.user_id !== userToDelete);
setUserData(updatedUserData);
}
} catch (error) {
console.error("Error deleting user:", error);
message.error("Failed to delete user");
}
}
setIsDeleteModalOpen(false);
setUserToDelete(null);
};

const cancelDelete = () => {
setIsDeleteModalOpen(false);
setUserToDelete(null);
};

const handleEditCancel = async () => {
setSelectedUser(null);
setEditModalVisible(false);
Expand Down Expand Up @@ -272,6 +306,12 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
>
View Keys
</Icon>
<Icon
icon={TrashIcon}
onClick={() => handleDelete(user.user_id)}
>
Delete
</Icon>
</TableCell>
</TableRow>
))}
Expand All @@ -293,6 +333,53 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
user={selectedUser}
onSubmit={handleEditSubmit}
/>
{isDeleteModalOpen && (
<div className="fixed z-10 inset-0 overflow-y-auto">
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div
className="fixed inset-0 transition-opacity"
aria-hidden="true"
>
<div className="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>

{/* Modal Panel */}
<span
className="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true"
>
&#8203;
</span>

{/* Confirmation Modal Content */}
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start">
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 className="text-lg leading-6 font-medium text-gray-900">
Delete User
</h3>
<div className="mt-2">
<p className="text-sm text-gray-500">
Are you sure you want to delete this user?
</p>
<p className="text-sm font-medium text-gray-900 mt-2">
User ID: {userToDelete}
</p>
</div>
</div>
</div>
</div>
<div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<Button onClick={confirmDelete} color="red" className="ml-2">
Delete
</Button>
<Button onClick={cancelDelete}>Cancel</Button>
</div>
</div>
</div>
</div>
)}
</Card>
{renderPagination()}
</Grid>
Expand Down