diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.test.tsx
index 7d9bee735b4..26d595f4d74 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.test.tsx
@@ -754,6 +754,19 @@ describe("UsagePage", () => {
});
});
+ it("should withhold the customer list while it is still loading", async () => {
+ mockUseCustomers.mockReturnValue({ data: undefined, isLoading: true, error: null } as any);
+
+ renderWithProviders();
+
+ act(() => {
+ fireEvent.change(screen.getByTestId("usage-view-select"), { target: { value: "customer" } });
+ });
+
+ const entityUsage = await screen.findByTestId("entity-usage");
+ expect(entityUsage).toHaveAttribute("data-entity-list", "null");
+ });
+
it("should show agent usage view for admins", async () => {
mockUseAgents.mockReturnValue({
data: { agents: mockAgents },
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.tsx
index 19d7a752938..cbdfc8f39e6 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsagePageView.tsx
@@ -97,7 +97,9 @@ const UsagePage: React.FC = ({ teams, organizations }) => {
});
const [fetchedTags, setFetchedTags] = useState | null>(null);
- const { data: customers = [] } = useCustomers();
+ // No [] default: an unresolved query must stay undefined so the customer
+ // filter reads as loading rather than as a range with no customers.
+ const { data: customers } = useCustomers();
const { data: agentsResponse } = useAgents();
const { data: currentUser } = useCurrentUser();
const isAdmin = all_admin_roles.includes(userRole || "");
diff --git a/ui/litellm-dashboard/src/components/EntityUsageExport/UsageExportHeader.test.tsx b/ui/litellm-dashboard/src/components/EntityUsageExport/UsageExportHeader.test.tsx
index 6c824459c08..52fc7605d90 100644
--- a/ui/litellm-dashboard/src/components/EntityUsageExport/UsageExportHeader.test.tsx
+++ b/ui/litellm-dashboard/src/components/EntityUsageExport/UsageExportHeader.test.tsx
@@ -104,6 +104,28 @@ describe("UsageExportHeader", () => {
expect(screen.queryByPlaceholderText("Select tag to filter...")).not.toBeInTheDocument();
});
+ it("should stay usable when a carried-over selection outlives its options", async () => {
+ const user = userEvent.setup();
+ const onFiltersChange = vi.fn();
+ renderWithProviders(
+ ,
+ );
+
+ expect(screen.getByPlaceholderText("No tags with usage in this range")).toBeEnabled();
+
+ await user.click(screen.getByRole("button", { name: "Clear Filter by tag" }));
+ expect(onFiltersChange).toHaveBeenCalledWith([]);
+ });
+
it("should leave the filter enabled with its normal placeholder when options exist", () => {
renderWithProviders(
= ({
const labelOf = (value: string) => filterOptions.find((option) => option.value === value)?.label ?? value;
const hasNoOptions = filterOptions.length === 0;
const emptyPlaceholder = `No ${entityType}s with usage in this range`;
+ // A selection carried over from a range that did have options still scopes
+ // the data below, so the control has to stay usable long enough to clear it.
+ const isFilterDisabled = hasNoOptions && selectedFilters.length === 0;
const filterList = (
@@ -76,7 +79,7 @@ const UsageExportHeader: React.FC = ({
const builtInFilter = (
onFiltersChange?.(next)}