mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix(ui): clean usage links for sessions without the organizations query
The organizations query only runs for premium sessions, and a disabled query stays pending forever, so the URL cleanup never ran for other sessions. Gate on isLoading instead, and give the access denied toast an id so a re-run effect shows it once
This commit is contained in:
parent
8cf03d6fd9
commit
a46433f3cb
4 changed files with 18 additions and 6 deletions
|
|
@ -170,7 +170,7 @@ vi.mock("@/app/(dashboard)/hooks/useIsOrgAdmin", () => ({
|
|||
}));
|
||||
|
||||
vi.mock("@/app/(dashboard)/hooks/organizations/useOrganizations", () => ({
|
||||
useOrganizations: vi.fn(() => ({ data: [], isPending: false })),
|
||||
useOrganizations: vi.fn(() => ({ data: undefined, isPending: true, isLoading: false })),
|
||||
}));
|
||||
|
||||
vi.mock("@/app/(dashboard)/hooks/users/useCurrentUser", () => ({
|
||||
|
|
@ -1495,7 +1495,9 @@ describe("UsagePage", () => {
|
|||
await waitFor(() => {
|
||||
expect(lastQuery()).toBe("?range=30d");
|
||||
});
|
||||
expect(warning).toHaveBeenCalledWith("You don't have access to that usage view");
|
||||
expect(warning).toHaveBeenCalledWith("You don't have access to that usage view", {
|
||||
id: "usage-url-access-denied",
|
||||
});
|
||||
expect(screen.queryByTestId("entity-usage")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
|
@ -1508,7 +1510,9 @@ describe("UsagePage", () => {
|
|||
await waitFor(() => {
|
||||
expect(lastQuery()).toBe("");
|
||||
});
|
||||
expect(warning).toHaveBeenCalledWith("You don't have access to that usage view");
|
||||
expect(warning).toHaveBeenCalledWith("You don't have access to that usage view", {
|
||||
id: "usage-url-access-denied",
|
||||
});
|
||||
expect(mockUserDailyActivityAggregatedCall).not.toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
|
|
|
|||
|
|
@ -150,17 +150,18 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
|
|||
// For non-admins or "my-usage" view, always pass their own user_id
|
||||
const effectiveUserId = usageView === "my-usage" || !isAdmin ? userID || null : urlParams.user;
|
||||
|
||||
const { isPending: organizationsPending } = useOrganizations();
|
||||
const { isLoading: organizationsLoading } = useOrganizations();
|
||||
const allowedViews = useMemo(
|
||||
() => allowedUsageOptions(userRole, canViewTagUsage, isOrgAdmin),
|
||||
[userRole, canViewTagUsage, isOrgAdmin],
|
||||
);
|
||||
const accessSettled = userRole !== null && !organizationsPending;
|
||||
const accessSettled = userRole !== null && !organizationsLoading;
|
||||
useEffect(() => {
|
||||
if (!accessSettled) return;
|
||||
const cleanup = cleanUsageUrl(urlParams, { allowedViews, isAdmin });
|
||||
if (!cleanup) return;
|
||||
if (cleanup.deniedAccess) toast.warning("You don't have access to that usage view");
|
||||
if (cleanup.deniedAccess)
|
||||
toast.warning("You don't have access to that usage view", { id: "usage-url-access-denied" });
|
||||
void setUrlParams(cleanup.patch);
|
||||
}, [accessSettled, urlParams, allowedViews, isAdmin, setUrlParams]);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@ describe("toast", () => {
|
|||
expect(sonner.success).toHaveBeenCalledWith("saved", { description: "Model x", duration: 1500 });
|
||||
});
|
||||
|
||||
it("passes an id through so repeated toasts collapse into one", () => {
|
||||
toast.warning("no access", { id: "denied" });
|
||||
expect(sonner.warning).toHaveBeenCalledWith("no access", expect.objectContaining({ id: "denied" }));
|
||||
});
|
||||
|
||||
it("dismiss clears every toast", () => {
|
||||
toast.dismiss();
|
||||
expect(sonner.dismiss).toHaveBeenCalledWith();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export type ToastKind = "success" | "info" | "warning" | "error";
|
|||
export type ToastOptions = {
|
||||
readonly description?: ReactNode;
|
||||
readonly durationMs?: number;
|
||||
readonly id?: string;
|
||||
};
|
||||
|
||||
type ErrorFacts = {
|
||||
|
|
@ -127,6 +128,7 @@ const titleFor = ({ status, proxyType }: ErrorFacts): string => {
|
|||
|
||||
const show = (kind: ToastKind, message: ReactNode, options?: ToastOptions): void => {
|
||||
sonner[kind](message, {
|
||||
id: options?.id,
|
||||
description: options?.description,
|
||||
duration: options?.durationMs ?? DEFAULT_DURATION_MS[kind],
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue