diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.tsx
index 97a9272f1e..7e9901d8bd 100644
--- a/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.tsx
+++ b/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.tsx
@@ -96,7 +96,9 @@ export function RequestResponsePanel({
{hasResponse ? (
-
+
+
+
) : (
Response data not available
)}
diff --git a/ui/litellm-dashboard/src/components/view_users/user_info_view.test.tsx b/ui/litellm-dashboard/src/components/view_users/user_info_view.test.tsx
index 8d5927124a..3dd814143e 100644
--- a/ui/litellm-dashboard/src/components/view_users/user_info_view.test.tsx
+++ b/ui/litellm-dashboard/src/components/view_users/user_info_view.test.tsx
@@ -1,4 +1,4 @@
-import { render } from "@testing-library/react";
+import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import UserInfoView from "./user_info_view";
@@ -41,19 +41,24 @@ describe("UserInfoView", () => {
possibleUIRoles: null,
};
- it("should render the loading state and then the user email", async () => {
- const { getByText, findAllByText } = render(
);
+ it("should render the loading state", () => {
+ render(
);
- expect(getByText("Loading user data...")).toBeInTheDocument();
-
- const emails = await findAllByText("test@example.com");
- expect(emails.length).toBeGreaterThan(0);
+ expect(screen.getByText("Loading user data...")).toBeInTheDocument();
});
- it("should render the user alias", async () => {
- const { findAllByText } = render(
);
+ it("should render the user email after loading", async () => {
+ render(
);
- const aliases = await findAllByText("Test Alias");
+ const emails = await screen.findAllByText("test@example.com");
+ expect(emails.length).toBeGreaterThan(0);
+ expect(screen.queryByText("Loading user data...")).not.toBeInTheDocument();
+ });
+
+ it("should render the user alias after loading", async () => {
+ render(
);
+
+ const aliases = await screen.findAllByText("Test Alias");
expect(aliases.length).toBeGreaterThan(0);
});
});
diff --git a/ui/litellm-dashboard/src/components/view_users/user_info_view.tsx b/ui/litellm-dashboard/src/components/view_users/user_info_view.tsx
index 2caae7d861..c377b9c4cd 100644
--- a/ui/litellm-dashboard/src/components/view_users/user_info_view.tsx
+++ b/ui/litellm-dashboard/src/components/view_users/user_info_view.tsx
@@ -17,6 +17,7 @@ import { formatNumberWithCommas, copyToClipboard as utilCopyToClipboard } from "
import { CopyIcon, CheckIcon } from "lucide-react";
import NotificationsManager from "../molecules/notifications_manager";
import { getBudgetDurationLabel } from "../common_components/budget_duration_dropdown";
+import DeleteResourceModal from "../common_components/DeleteResourceModal";
interface UserInfoViewProps {
userId: string;
@@ -60,6 +61,7 @@ export default function UserInfoView({
}: UserInfoViewProps) {
const [userData, setUserData] = useState
(null);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
+ const [isDeletingUser, setIsDeletingUser] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [isEditing, setIsEditing] = useState(startInEditMode);
const [userModels, setUserModels] = useState([]);
@@ -115,6 +117,7 @@ export default function UserInfoView({
const handleDelete = async () => {
try {
if (!accessToken) return;
+ setIsDeletingUser(true);
await userDeleteCall(accessToken, [userId]);
NotificationsManager.success("User deleted successfully");
if (onDelete) {
@@ -124,9 +127,16 @@ export default function UserInfoView({
} catch (error) {
console.error("Error deleting user:", error);
NotificationsManager.fromBackend("Failed to delete user");
+ } finally {
+ setIsDeleteModalOpen(false);
+ setIsDeletingUser(false);
}
};
+ const cancelDelete = () => {
+ setIsDeleteModalOpen(false);
+ };
+
const handleUserUpdate = async (formValues: Record) => {
try {
if (!accessToken || !userData) return;
@@ -219,7 +229,7 @@ export default function UserInfoView({
icon={TrashIcon}
variant="secondary"
onClick={() => setIsDeleteModalOpen(true)}
- className="flex items-center"
+ className="flex items-center text-red-500 border-red-500 hover:text-red-600 hover:border-red-600"
>
Delete User
@@ -227,39 +237,33 @@ export default function UserInfoView({
)}
- {/* Delete Confirmation Modal */}
- {isDeleteModalOpen && (
-