diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.activity.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.activity.test.tsx
index ca7adf07941..2b0e65d9fd9 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.activity.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.activity.test.tsx
@@ -1,5 +1,13 @@
-import { fireEvent, render, waitFor } from "@testing-library/react";
-import { describe, expect, it, vi } from "vitest";
+import { fireEvent, waitFor } from "@testing-library/react";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+import { renderWithProviders, testQueryClient } from "../../../../../tests/test-utils";
+
+const { useAuthorizedMock } = vi.hoisted(() => ({ useAuthorizedMock: vi.fn() }));
+
+vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
+ default: useAuthorizedMock,
+}));
const mockUserDailyActivityCall = vi.fn();
@@ -36,10 +44,15 @@ const singlePage = {
};
describe("CostOptimizationView daily activity", () => {
+ beforeEach(() => {
+ testQueryClient.clear();
+ useAuthorizedMock.mockReturnValue({ accessToken: "test-token", userId: "u1", userRole: "proxy_admin" });
+ });
+
it("fetches daily activity once for the page and shares it with every tab that needs it", async () => {
mockUserDailyActivityCall.mockResolvedValue(singlePage);
- const { getByRole, getByTestId } = render(
+ const { getByRole, getByTestId } = renderWithProviders(
,
);
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.test.tsx
index c6d5a410418..ab3744a0383 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.test.tsx
@@ -1,6 +1,8 @@
-import { fireEvent, render } from "@testing-library/react";
+import { fireEvent } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
+import { renderWithProviders, testQueryClient } from "../../../../../tests/test-utils";
+
const { useAuthorizedMock } = vi.hoisted(() => ({ useAuthorizedMock: vi.fn() }));
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
@@ -19,11 +21,12 @@ import CostOptimizationView from "./CostOptimizationView";
const renderView = (userRole = "Admin") => {
useAuthorizedMock.mockReturnValue({ accessToken: "test-token", userId: "u1", userRole });
- return render();
+ return renderWithProviders();
};
describe("CostOptimizationView", () => {
beforeEach(() => {
+ testQueryClient.clear();
useAuthorizedMock.mockReturnValue({ accessToken: "test-token", userId: "u1", userRole: "Admin" });
});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx
index ad68111bba7..45d8b15bfb9 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx
@@ -1,10 +1,11 @@
-import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ToolSpendResponse } from "@/components/networking";
import type { DailyData, SpendMetrics } from "@/components/UsagePage/types";
+import { renderWithProviders, testQueryClient } from "../../../../../tests/test-utils";
+
const mockGetToolSpend = vi.fn();
const { useAuthorizedMock } = vi.hoisted(() => ({ useAuthorizedMock: vi.fn() }));
@@ -106,7 +107,7 @@ const renderWith = (results: DailyData[], options: RenderOptions = {}) => {
} = options;
mockGetToolSpend.mockResolvedValue(toolSpend);
useAuthorizedMock.mockReturnValue({ accessToken: "test-token", userId: "u1", userRole });
- return render(
+ return renderWithProviders(
JSON.parse(element.getAttribute("da
describe("UsageTab", () => {
beforeEach(() => {
+ testQueryClient.clear();
mockGetToolSpend.mockReset();
});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/organizations/useOrganizations.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/organizations/useOrganizations.ts
index 734c1986f8f..dc757d175af 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/organizations/useOrganizations.ts
+++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/organizations/useOrganizations.ts
@@ -10,7 +10,14 @@ export interface OrganizationListFilters {
org_alias?: string | null;
}
-export const useOrganizations = (filters?: OrganizationListFilters): UseQueryResult => {
+export interface UseOrganizationsOptions {
+ enabled?: boolean;
+}
+
+export const useOrganizations = (
+ filters?: OrganizationListFilters,
+ options?: UseOrganizationsOptions,
+): UseQueryResult => {
const { accessToken, userId, userRole } = useAuthorized();
const orgId = filters?.org_id || null;
const orgAlias = filters?.org_alias || null;
@@ -21,7 +28,7 @@ export const useOrganizations = (filters?: OrganizationListFilters): UseQueryRes
: {},
),
queryFn: async () => await organizationListCall(accessToken!, orgId, orgAlias),
- enabled: Boolean(accessToken && userId && userRole),
+ enabled: (options?.enabled ?? true) && Boolean(accessToken && userId && userRole),
});
};
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useCan.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useCan.ts
index 13903007cae..3d7788432b8 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useCan.ts
+++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useCan.ts
@@ -1,13 +1,13 @@
"use client";
-import { hasCapability, type Capability } from "@/utils/capabilities";
+import { capabilityRequiresOrgAdmin, hasCapability, type Capability } from "@/utils/capabilities";
import useAuthorized from "./useAuthorized";
import useIsOrgAdmin from "./useIsOrgAdmin";
const useCan = (capability: Capability): boolean => {
const { userRole } = useAuthorized();
- const isOrgAdmin = useIsOrgAdmin();
+ const isOrgAdmin = useIsOrgAdmin({ enabled: capabilityRequiresOrgAdmin(capability) });
return hasCapability(userRole, capability, isOrgAdmin);
};
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useIsOrgAdmin.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useIsOrgAdmin.ts
index d93b57a3fc3..61bc68ca250 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useIsOrgAdmin.ts
+++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useIsOrgAdmin.ts
@@ -5,9 +5,16 @@ import { isOrgAdminForAnyOrg, isOrgAdminSessionRole } from "@/utils/roles";
import { useOrganizations } from "./organizations/useOrganizations";
import useAuthorized from "./useAuthorized";
-const useIsOrgAdmin = (): boolean => {
+export interface UseIsOrgAdminOptions {
+ enabled?: boolean;
+}
+
+const useIsOrgAdmin = (options?: UseIsOrgAdminOptions): boolean => {
const { userId, userRole } = useAuthorized();
- const { data: organizations } = useOrganizations();
+ const { data: organizations } = useOrganizations(undefined, options);
+ if (options?.enabled === false) {
+ return isOrgAdminSessionRole(userRole);
+ }
return isOrgAdminSessionRole(userRole) || isOrgAdminForAnyOrg(organizations, userId);
};
diff --git a/ui/litellm-dashboard/src/utils/capabilities.ts b/ui/litellm-dashboard/src/utils/capabilities.ts
index dabbc827bcb..abb130bd255 100644
--- a/ui/litellm-dashboard/src/utils/capabilities.ts
+++ b/ui/litellm-dashboard/src/utils/capabilities.ts
@@ -21,6 +21,8 @@ export type Capability = keyof typeof CAPABILITY_ROLES;
const ORG_ADMIN_CAPABILITIES: ReadonlySet = new Set(["viewDeletedTeams"]);
+export const capabilityRequiresOrgAdmin = (capability: Capability): boolean => ORG_ADMIN_CAPABILITIES.has(capability);
+
export const hasCapability = (
userRole: string | null | undefined,
capability: Capability,