refactor(ui): give each Cost Optimization tab its own route

Split the Cost Optimization page's four tabs (Usage, Prompt Compression,
Autorouter, Prompt Caching) into their own prerendered paths under
/cost-optimization, mirroring the Models + Endpoints and Caching per-tab
routing. A shared layout renders the header, the experimental banner and
the tab bar, deriving the active tab from the pathname; each tab is its
own page.tsx that pulls only the props it needs, so deep links and
hard-loads to /cost-optimization/compression, /autorouter and /caching
resolve to real static HTML with no nginx change.

The former CostOptimizationView is removed. The tab bar is rebuilt on the
shadcn Tabs primitive and the antd Alert becomes a plain banner, so the
page adds no new antd usage.
This commit is contained in:
ryan-crabbe-berri 2026-07-23 13:36:36 -07:00
parent a507394841
commit d7e31e73c0
10 changed files with 252 additions and 117 deletions

View file

@ -1,34 +0,0 @@
import { fireEvent, render } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
vi.mock("./UsageTab", () => ({ __esModule: true, default: () => <div data-testid="usage-tab" /> }));
vi.mock("./PromptCompressionTab", () => ({ __esModule: true, default: () => <div data-testid="compression-tab" /> }));
vi.mock("./AutorouterTab", () => ({ __esModule: true, default: () => <div data-testid="autorouter-tab" /> }));
vi.mock("./PromptCachingTab", () => ({ __esModule: true, default: () => <div data-testid="caching-tab" /> }));
import CostOptimizationView from "./CostOptimizationView";
const renderView = () => render(<CostOptimizationView accessToken="test-token" userId="u1" userRole="proxy_admin" />);
describe("CostOptimizationView", () => {
it("renders all four cost-optimization tabs", () => {
const { getByText } = renderView();
expect(getByText("Usage")).toBeInTheDocument();
expect(getByText("Prompt Compression")).toBeInTheDocument();
expect(getByText("Autorouter")).toBeInTheDocument();
expect(getByText("Prompt Caching")).toBeInTheDocument();
});
it("defaults to the Usage tab and switches the active tab on click", () => {
const { getByRole } = renderView();
expect(getByRole("tab", { name: "Usage" })).toHaveAttribute("aria-selected", "true");
expect(getByRole("tab", { name: "Prompt Compression" })).toHaveAttribute("aria-selected", "false");
fireEvent.click(getByRole("tab", { name: "Prompt Compression" }));
expect(getByRole("tab", { name: "Usage" })).toHaveAttribute("aria-selected", "false");
expect(getByRole("tab", { name: "Prompt Compression" })).toHaveAttribute("aria-selected", "true");
});
});

View file

@ -1,81 +0,0 @@
"use client";
import React from "react";
import { PiggyBank } from "lucide-react";
import { Alert, Tabs } from "antd";
import UsageTab from "./UsageTab";
import PromptCompressionTab from "./PromptCompressionTab";
import AutorouterTab from "./AutorouterTab";
import PromptCachingTab from "./PromptCachingTab";
import { useDailyActivityRange } from "./useDailyActivityRange";
interface CostOptimizationViewProps {
accessToken: string | null;
userId: string | null;
userRole: string;
}
const CostOptimizationView: React.FC<CostOptimizationViewProps> = ({ accessToken, userId, userRole }) => {
const activity = useDailyActivityRange(accessToken, userId, userRole);
const items = [
{
key: "usage",
label: "Usage",
children: <UsageTab accessToken={accessToken} activity={activity} />,
},
{
key: "compression",
label: "Prompt Compression",
children: <PromptCompressionTab accessToken={accessToken} />,
},
{
key: "autorouter",
label: "Autorouter",
children: <AutorouterTab accessToken={accessToken} userId={userId} userRole={userRole} />,
},
{
key: "caching",
label: "Prompt Caching",
children: <PromptCachingTab accessToken={accessToken} activity={activity} />,
},
];
return (
<div className="w-full space-y-6 p-6">
<div>
<div className="flex items-center gap-2">
<PiggyBank className="size-6 text-emerald-600" strokeWidth={1.75} />
<h1 className="text-xl font-semibold text-foreground">Cost Optimization</h1>
</div>
<p className="mt-1 text-sm text-muted-foreground">
Track and configure the mechanisms that save you money: prompt compression, prompt caching, and auto routing
</p>
</div>
<Alert
type="info"
showIcon
message="This is an experimental dashboard"
description={
<span>
Have feedback? Join the discussion{" "}
<a
href="https://github.com/BerriAI/litellm/discussions/32172"
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 underline"
>
here
</a>
</span>
}
/>
<Tabs defaultActiveKey="usage" items={items} />
</div>
);
};
export default CostOptimizationView;

View file

@ -0,0 +1,9 @@
"use client";
import AutorouterTab from "@/app/(dashboard)/cost-optimization/_components/AutorouterTab";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
export default function AutorouterPage() {
const { accessToken, userId, userRole } = useAuthorized();
return <AutorouterTab accessToken={accessToken} userId={userId} userRole={userRole} />;
}

View file

@ -0,0 +1,9 @@
"use client";
import PromptCachingTab from "@/app/(dashboard)/cost-optimization/_components/PromptCachingTab";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
export default function PromptCachingPage() {
const { accessToken } = useAuthorized();
return <PromptCachingTab accessToken={accessToken} />;
}

View file

@ -0,0 +1,9 @@
"use client";
import PromptCompressionTab from "@/app/(dashboard)/cost-optimization/_components/PromptCompressionTab";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
export default function PromptCompressionPage() {
const { accessToken } = useAuthorized();
return <PromptCompressionTab accessToken={accessToken} />;
}

View file

@ -0,0 +1,82 @@
/* @vitest-environment jsdom */
import { act, render } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import CostOptimizationLayout from "./layout";
const { mockPush, navState } = vi.hoisted(() => ({
mockPush: vi.fn(),
navState: { pathname: "/cost-optimization" },
}));
vi.mock("next/navigation", () => ({
usePathname: () => navState.pathname,
useRouter: () => ({ push: mockPush }),
}));
vi.mock("@/components/networking", () => ({ serverRootPath: "" }));
const renderLayout = () =>
render(
<CostOptimizationLayout>
<div data-testid="tab-content">CHILD</div>
</CostOptimizationLayout>,
);
describe("CostOptimizationLayout", () => {
beforeEach(() => {
navState.pathname = "/cost-optimization";
mockPush.mockClear();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(global as any).ResizeObserver = class {
observe() {}
unobserve() {}
disconnect() {}
};
});
it("renders the tab bar and the active tab's page content", () => {
const { getByRole, getByTestId } = renderLayout();
expect(getByRole("tab", { name: "Usage" })).toBeInTheDocument();
expect(getByRole("tab", { name: "Prompt Compression" })).toBeInTheDocument();
expect(getByRole("tab", { name: "Autorouter" })).toBeInTheDocument();
expect(getByRole("tab", { name: "Prompt Caching" })).toBeInTheDocument();
expect(getByTestId("tab-content")).toHaveTextContent("CHILD");
});
it("marks the base route's Usage tab active", () => {
const { getByRole } = renderLayout();
expect(getByRole("tab", { name: "Usage" })).toHaveAttribute("aria-selected", "true");
expect(getByRole("tab", { name: "Prompt Compression" })).toHaveAttribute("aria-selected", "false");
});
it("navigates to a tab's path when its tab is clicked", async () => {
const { getByRole } = renderLayout();
await act(async () => {
getByRole("tab", { name: "Prompt Compression" }).click();
});
expect(mockPush).toHaveBeenCalledWith(expect.stringMatching(/\/cost-optimization\/compression\/$/));
});
it("routes the base tab back to the cost-optimization root (no slug)", async () => {
navState.pathname = "/cost-optimization/autorouter";
const { getByRole } = renderLayout();
await act(async () => {
getByRole("tab", { name: "Usage" }).click();
});
expect(mockPush).toHaveBeenCalledWith(expect.stringMatching(/\/cost-optimization\/$/));
});
it("redirects to the base cost-optimization path when the tab slug is unknown", async () => {
const replaceMock = vi.fn();
const originalLocation = window.location;
Object.defineProperty(window, "location", {
configurable: true,
value: { replace: replaceMock, assign: vi.fn(), href: "http://localhost/", pathname: "/", search: "" },
});
navState.pathname = "/cost-optimization/bogus";
await act(async () => {
renderLayout();
});
expect(replaceMock).toHaveBeenCalledWith(expect.stringMatching(/\/cost-optimization\/$/));
Object.defineProperty(window, "location", { configurable: true, value: originalLocation });
});
});

View file

@ -0,0 +1,82 @@
"use client";
import type { ReactNode } from "react";
import { useEffect } from "react";
import { usePathname, useRouter } from "next/navigation";
import { PiggyBank, Info } from "lucide-react";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
costOptimizationTabHref,
slugFromPathname,
type CostOptimizationTabSlug,
} from "@/app/(dashboard)/cost-optimization/tabRoutes";
const BASE_TAB_KEY = "usage";
const ORDERED_KEYS: Array<"" | CostOptimizationTabSlug> = ["", "compression", "autorouter", "caching"];
const TAB_LABELS: Record<"" | CostOptimizationTabSlug, string> = {
"": "Usage",
compression: "Prompt Compression",
autorouter: "Autorouter",
caching: "Prompt Caching",
};
export default function CostOptimizationLayout({ children }: { children: ReactNode }) {
const pathname = usePathname();
const router = useRouter();
const activeSlug = slugFromPathname(pathname);
const isKnownSlug = ORDERED_KEYS.some((slug) => slug === activeSlug);
const activeKey = isKnownSlug ? activeSlug || BASE_TAB_KEY : BASE_TAB_KEY;
useEffect(() => {
if (activeSlug !== "" && !isKnownSlug) {
window.location.replace(costOptimizationTabHref(""));
}
}, [activeSlug, isKnownSlug]);
return (
<div className="w-full space-y-6 p-6">
<div>
<div className="flex items-center gap-2">
<PiggyBank className="size-6 text-emerald-600" strokeWidth={1.75} />
<h1 className="text-xl font-semibold text-foreground">Cost Optimization</h1>
</div>
<p className="mt-1 text-sm text-muted-foreground">
Track and configure the mechanisms that save you money: prompt compression, prompt caching, and auto routing
</p>
</div>
<div className="flex items-start gap-2 rounded-md border border-blue-200 bg-blue-50 p-3 text-sm text-blue-800 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-200">
<Info className="mt-0.5 size-4 shrink-0" />
<span>
This is an experimental dashboard. Have feedback? Join the discussion{" "}
<a
href="https://github.com/BerriAI/litellm/discussions/32172"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
here
</a>
</span>
</div>
<Tabs
value={activeKey}
onValueChange={(key) => router.push(costOptimizationTabHref(key === BASE_TAB_KEY ? "" : key))}
>
<TabsList variant="line">
{ORDERED_KEYS.map((slug) => (
<TabsTrigger key={slug || BASE_TAB_KEY} value={slug || BASE_TAB_KEY}>
{TAB_LABELS[slug]}
</TabsTrigger>
))}
</TabsList>
</Tabs>
<div>{children}</div>
</div>
);
}

View file

@ -1,9 +1,9 @@
"use client";
import CostOptimizationView from "./_components/CostOptimizationView";
import UsageTab from "./_components/UsageTab";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
export default function CostOptimizationPage() {
const { accessToken, userId, userRole } = useAuthorized();
return <CostOptimizationView accessToken={accessToken} userId={userId} userRole={userRole} />;
return <UsageTab accessToken={accessToken} userId={userId} userRole={userRole} />;
}

View file

@ -0,0 +1,38 @@
/* @vitest-environment jsdom */
import { describe, expect, it, vi } from "vitest";
vi.mock("@/components/networking", () => ({ serverRootPath: "" }));
import { COST_OPTIMIZATION_TAB_SLUGS, costOptimizationTabHref, slugFromPathname } from "./tabRoutes";
describe("slugFromPathname", () => {
it("returns empty string for the base path with or without a trailing slash", () => {
expect(slugFromPathname("/cost-optimization")).toBe("");
expect(slugFromPathname("/cost-optimization/")).toBe("");
});
it("extracts the tab slug from dev and proxy-mounted (/ui) paths", () => {
expect(slugFromPathname("/cost-optimization/autorouter")).toBe("autorouter");
expect(slugFromPathname("/ui/cost-optimization/compression/")).toBe("compression");
});
it("returns the raw segment for an unknown tab so the layout can redirect to base", () => {
expect(slugFromPathname("/ui/cost-optimization/bogus")).toBe("bogus");
});
it("returns empty string when the cost-optimization base segment is not in the path", () => {
expect(slugFromPathname("/teams")).toBe("");
});
});
describe("costOptimizationTabHref", () => {
it("builds the trailing-slash base href for the empty slug", () => {
expect(costOptimizationTabHref("")).toBe("/ui/cost-optimization/");
});
it("builds a trailing-slash href for every tab slug (required by static export)", () => {
for (const slug of COST_OPTIMIZATION_TAB_SLUGS) {
expect(costOptimizationTabHref(slug)).toBe(`/ui/cost-optimization/${slug}/`);
}
});
});

View file

@ -0,0 +1,21 @@
import { migratedHref } from "@/utils/migratedPages";
export const COST_OPTIMIZATION_BASE_SEGMENT = "cost-optimization";
export const COST_OPTIMIZATION_TAB_SLUGS = ["compression", "autorouter", "caching"] as const;
export type CostOptimizationTabSlug = (typeof COST_OPTIMIZATION_TAB_SLUGS)[number];
export function costOptimizationTabHref(slug: string): string {
const base = migratedHref(COST_OPTIMIZATION_BASE_SEGMENT);
return slug ? `${base}/${slug}/` : `${base}/`;
}
export function slugFromPathname(pathname: string): string {
const parts = pathname.split("/").filter(Boolean);
const idx = parts.indexOf(COST_OPTIMIZATION_BASE_SEGMENT);
if (idx === -1) {
return "";
}
return parts[idx + 1] ?? "";
}