";
} else if (selectedProvider == Providers.FalAI) {
return "fal_ai/fal-ai/flux-pro/v1.1-ultra";
+ } else if (selectedProvider == Providers.RunwayML) {
+ return "runwayml/gen4_turbo";
} else {
return "gpt-3.5-turbo";
}
diff --git a/ui/litellm-dashboard/src/components/public_model_hub.tsx b/ui/litellm-dashboard/src/components/public_model_hub.tsx
index b1a0cc9021..c32c7462a5 100644
--- a/ui/litellm-dashboard/src/components/public_model_hub.tsx
+++ b/ui/litellm-dashboard/src/components/public_model_hub.tsx
@@ -14,7 +14,6 @@ import { getProviderLogoAndName } from "./provider_info_helpers";
import Navbar from "./navbar";
import { ThemeProvider } from "@/contexts/ThemeContext";
import NotificationsManager from "./molecules/notifications_manager";
-// Simple approach without react-markdown dependency
interface ModelGroupInfo {
model_group: string;
diff --git a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx
index d90b9e32f7..caa9729d4e 100644
--- a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx
+++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx
@@ -1,6 +1,6 @@
import GuardrailSelector from "@/components/guardrails/GuardrailSelector";
import { TextInput, Button as TremorButton } from "@tremor/react";
-import { Button as AntdButton, Form, Input, Select, Tooltip } from "antd";
+import { Form, Input, Select, Tooltip } from "antd";
import { useEffect, useState } from "react";
import { mapInternalToDisplayNames } from "../callback_info_helpers";
import KeyLifecycleSettings from "../common_components/KeyLifecycleSettings";
@@ -548,7 +548,9 @@ export function KeyEditView({
-
Cancel
+
+ Cancel
+
Save Changes
diff --git a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx
index 411d73a90c..58b8363e30 100644
--- a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx
+++ b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx
@@ -545,9 +545,7 @@ export default function KeyInfoView({
Key Settings
{!isEditing && userRole && rolesWithWriteAccess.includes(userRole) && (
-
+
)}
diff --git a/ui/litellm-dashboard/src/components/view_users/columns.tsx b/ui/litellm-dashboard/src/components/view_users/columns.tsx
index 8946aac228..cb6c54d4d5 100644
--- a/ui/litellm-dashboard/src/components/view_users/columns.tsx
+++ b/ui/litellm-dashboard/src/components/view_users/columns.tsx
@@ -109,17 +109,32 @@ export const columns = (
},
{
id: "actions",
- header: "",
+ header: "Actions",
cell: ({ row }) => (
-
- handleUserClick(row.original.user_id, true)} />
+
+ handleUserClick(row.original.user_id, true)}
+ className="cursor-pointer hover:text-blue-600"
+ />
-
- handleDelete(row.original.user_id)} />
+
+ handleDelete(row.original.user_id)}
+ className="cursor-pointer hover:text-red-600"
+ />
-
- handleResetPassword(row.original.user_id)} />
+
+ handleResetPassword(row.original.user_id)}
+ className="cursor-pointer hover:text-green-600"
+ />
),
diff --git a/ui/litellm-dashboard/src/components/view_users/table.test.tsx b/ui/litellm-dashboard/src/components/view_users/table.test.tsx
new file mode 100644
index 0000000000..5b612b2732
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/view_users/table.test.tsx
@@ -0,0 +1,46 @@
+import { render } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import React from "react";
+
+import { UserDataTable } from "./table";
+
+describe("UserDataTable", () => {
+ it("should render the UserDataTable component", () => {
+ const filters = {
+ email: "",
+ user_id: "",
+ user_role: "",
+ sso_user_id: "",
+ team: "",
+ model: "",
+ min_spend: null,
+ max_spend: null,
+ sort_by: "",
+ sort_order: "asc" as const,
+ };
+
+ const updateFilters = vi.fn();
+
+ const { getByText } = render(
+ ,
+ );
+
+ expect(getByText("Filters")).toBeInTheDocument();
+ });
+});
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
new file mode 100644
index 0000000000..7334dd72fc
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/view_users/user_info_view.test.tsx
@@ -0,0 +1,51 @@
+import { render } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import UserInfoView from "./user_info_view";
+
+vi.mock("../networking", () => {
+ const MOCK_USER_DATA = {
+ user_id: "user-123",
+ user_info: {
+ user_email: "test@example.com",
+ user_role: "admin",
+ teams: [],
+ models: [],
+ max_budget: 100,
+ budget_duration: "30d",
+ spend: 0,
+ metadata: {},
+ created_at: "2025-01-01T00:00:00.000Z",
+ updated_at: "2025-01-02T00:00:00.000Z",
+ },
+ keys: [],
+ teams: [],
+ };
+
+ return {
+ userInfoCall: vi.fn().mockResolvedValue(MOCK_USER_DATA),
+ userDeleteCall: vi.fn(),
+ userUpdateUserCall: vi.fn(),
+ modelAvailableCall: vi.fn().mockResolvedValue({ data: [] }),
+ invitationCreateCall: vi.fn(),
+ getProxyBaseUrl: () => "https://litellm.test",
+ };
+});
+
+describe("UserInfoView", () => {
+ const defaultProps = {
+ userId: "user-123",
+ onClose: vi.fn(),
+ accessToken: "test-token",
+ userRole: null,
+ possibleUIRoles: null,
+ };
+
+ it("renders loading state and then the user email", async () => {
+ const { getByText, findAllByText } = render();
+
+ expect(getByText("Loading user data...")).toBeInTheDocument();
+
+ const emails = await findAllByText("test@example.com");
+ expect(emails.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 ab2c177707..65aa7c9d4c 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
@@ -343,9 +343,7 @@ export default function UserInfoView({
User Settings
{!isEditing && userRole && rolesWithWriteAccess.includes(userRole) && (
-
+
)}