From 108c22115e45f7e8a2632b93d5b5e4d1a1177940 Mon Sep 17 00:00:00 2001 From: Gaurav Sharma Date: Thu, 15 Oct 2026 23:42:09 +0530 Subject: [PATCH] feat(ui): show temporary budget increase on the My User tab A team member could not see their own temporary budget increase: the members/me response already carries it, but the My User tab dropped it. Show the increase and its expiry under the budget line while it is still in effect; once it expires the line goes away. --- .../src/components/team/MyUserTab.test.tsx | 50 +++++++++++++++++++ .../src/components/team/MyUserTab.tsx | 18 +++++++ .../src/components/team/useMyTeamMember.ts | 2 + 3 files changed, 70 insertions(+) diff --git a/ui/litellm-dashboard/src/components/team/MyUserTab.test.tsx b/ui/litellm-dashboard/src/components/team/MyUserTab.test.tsx index d9ba3d82230..aebc474c340 100644 --- a/ui/litellm-dashboard/src/components/team/MyUserTab.test.tsx +++ b/ui/litellm-dashboard/src/components/team/MyUserTab.test.tsx @@ -46,4 +46,54 @@ describe("MyUserTab", () => { expect(screen.getByText("model-one")).toBeInTheDocument(); expect(screen.getByText("TPM: 1,000")).toBeInTheDocument(); }); + + describe("temporary budget increase", () => { + const renderWithTempBudget = (temp: { + temp_budget_increase?: number | null; + temp_budget_expiry?: string | null; + }) => { + vi.mocked(useMyTeamMember).mockReturnValue({ + data: { + user_id: "user-1", + team_id: "team-1", + role: "user", + spend: 12.5, + total_spend: 30, + litellm_budget_table: { max_budget: 100, ...temp }, + }, + isLoading: false, + error: null, + } as ReturnType); + + renderWithProviders(); + }; + + it("shows an active increase and its expiry date under the budget", () => { + renderWithTempBudget({ temp_budget_increase: 150, temp_budget_expiry: "2099-11-01T12:00:00Z" }); + + expect(screen.getByTestId("my-temp-budget")).toHaveTextContent("+ $150.00 temporary increase until Nov 1, 2099"); + }); + + it("treats an expiry without a timezone as UTC, as the API returns it", () => { + renderWithTempBudget({ temp_budget_increase: 150, temp_budget_expiry: "2099-11-01T12:00:00" }); + + expect(screen.getByTestId("my-temp-budget")).toBeInTheDocument(); + }); + + it("shows nothing once the increase has expired", () => { + renderWithTempBudget({ temp_budget_increase: 150, temp_budget_expiry: "2020-01-11T00:00:00Z" }); + + expect(screen.queryByTestId("my-temp-budget")).not.toBeInTheDocument(); + }); + + it("shows nothing without an increase, with a 0 increase, or without an expiry", () => { + renderWithTempBudget({ temp_budget_increase: 0, temp_budget_expiry: "2099-11-01T12:00:00Z" }); + expect(screen.queryByTestId("my-temp-budget")).not.toBeInTheDocument(); + }); + + it("shows nothing when the expiry is missing", () => { + renderWithTempBudget({ temp_budget_increase: 150, temp_budget_expiry: null }); + expect(screen.queryByTestId("my-temp-budget")).not.toBeInTheDocument(); + }); + }); }); diff --git a/ui/litellm-dashboard/src/components/team/MyUserTab.tsx b/ui/litellm-dashboard/src/components/team/MyUserTab.tsx index 9aae6a25dc2..bb502e8dfe5 100644 --- a/ui/litellm-dashboard/src/components/team/MyUserTab.tsx +++ b/ui/litellm-dashboard/src/components/team/MyUserTab.tsx @@ -1,5 +1,6 @@ import { formatBudgetReset } from "@/utils/budgetUtils"; import { formatNumberWithCommas } from "@/utils/dataUtils"; +import { tempBudgetExpiryMs } from "@/components/shared/InheritedBudgetHint"; import { SimpleTooltip } from "@/components/ui/tooltip"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; @@ -30,6 +31,17 @@ const formatRateLimit = (value: number | null | undefined): string => { return formatNumberWithCommas(value, 0); }; +// The member's temporary increase while it is still in effect, with its expiry formatted for display. +const activeTempBudget = ( + budgetTable: Parameters[0], + now: Date = new Date(), +): { amount: number; expiry: string | null } | null => { + const amount = budgetTable?.temp_budget_increase ?? 0; + const expiryMs = tempBudgetExpiryMs(budgetTable); + if (amount <= 0 || expiryMs === null || expiryMs <= now.getTime()) return null; + return { amount, expiry: formatBudgetReset(new Date(expiryMs).toISOString()) }; +}; + export default function MyUserTab({ teamId }: MyUserTabProps) { const { data, isLoading, error } = useMyTeamMember(teamId); @@ -69,6 +81,7 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { const rpmLimit = budgetTable?.rpm_limit ?? null; const budgetReset = formatBudgetReset(budgetTable?.budget_reset_at); const allowedModels = budgetTable?.allowed_models ?? null; + const tempBudget = activeTempBudget(budgetTable); return (
@@ -103,6 +116,11 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { of {maxBudget === null ? "Unlimited" : `$${formatNumber(maxBudget, 4)}`}
+ {tempBudget && ( +
+ + ${formatNumber(tempBudget.amount, 2)} temporary increase until {tempBudget.expiry} +
+ )} {budgetReset &&
Resets {budgetReset}
} diff --git a/ui/litellm-dashboard/src/components/team/useMyTeamMember.ts b/ui/litellm-dashboard/src/components/team/useMyTeamMember.ts index 9eec1f135bd..1cce9c27bfa 100644 --- a/ui/litellm-dashboard/src/components/team/useMyTeamMember.ts +++ b/ui/litellm-dashboard/src/components/team/useMyTeamMember.ts @@ -22,6 +22,8 @@ export interface TeamMemberInfo { budget_duration?: string | null; budget_reset_at?: string | null; allowed_models?: string[] | null; + temp_budget_increase?: number | null; + temp_budget_expiry?: string | null; } | null; }