feat(site/src/pages/AISettingsPage/SpendPage): announce cost controls move to AI Governance (#27543)

Adds an informational banner to the AI settings **Spend** tab announcing
that cost controls features move to AI Governance in v2.37, with a link
to the AI Gateway cost controls docs.

Banner copy:

> Cost controls features will move to AI Governance in v2.37. [Read more
here](https://coder.com/docs/ai-coder/ai-gateway/cost-controls)

The link uses the existing `docs()` helper from `#/utils/docs`, so it
resolves the deployment's configured `docs-url` meta tag and otherwise
falls back to a version-pinned `coder.com/docs/@<version>` URL. This
matches how sibling AI settings pages link out (for example
`GatewayKeysPageView` and `ProvidersPageView`).

It points at `/ai-coder/ai-gateway/cost-controls`, the AI Gateway Cost
Controls page added in #27570. That page is present on `main` and the
URL resolves, so the banner links to live documentation.

The banner renders on the main Spend tab. It is intentionally not shown
in the per-user spend drill-in sub-view, which returns early from a
separate component.

## Validation

- Extended the existing `SpendWithLimitsAndUsers` story to assert the
banner copy and the resolved docs `href`. Both assertions were verified
to fail when the banner is removed and when the link points somewhere
else.
- Storybook story tests for `SpendPageView.stories.tsx` pass (12 tests),
plus `tsc -p .`, `biome check`, and `lint:compiler` clean.
- `make pre-commit` passed through the git hooks.

> Mux opened this PR on Mike's behalf.
This commit is contained in:
Michael Suchacz
2026-07-28 12:32:14 +02:00
committed by GitHub
parent e96e7cfec2
commit e83f018f5f
2 changed files with 27 additions and 0 deletions
@@ -300,6 +300,17 @@ export const SpendWithLimitsAndUsers: Story = {
const canvas = within(canvasElement);
await canvas.findByText("Spend limits and usage");
await expect(
canvas.getByText(
/Cost controls features will move to AI Governance in v2\.37\./,
),
).toBeInTheDocument();
await expect(
canvas.getByRole("link", { name: /Read more here/ }),
).toHaveAttribute(
"href",
expect.stringContaining("/ai-coder/ai-gateway/cost-controls"),
);
await expect(
canvas.getByRole("switch", { name: "Spend limit" }),
).toBeInTheDocument();
@@ -1,9 +1,11 @@
import type { FC } from "react";
import { getErrorMessage } from "#/api/errors";
import type * as TypesGen from "#/api/typesGenerated";
import { Alert, AlertDescription } from "#/components/Alert/Alert";
import { ErrorAlert } from "#/components/Alert/ErrorAlert";
import { Button } from "#/components/Button/Button";
import type { DateRangeValue } from "#/components/DateRangePicker/DateRangePicker";
import { Link } from "#/components/Link/Link";
import type { PaginationResult } from "#/components/PaginationWidget/PaginationContainer";
import {
SettingsHeader,
@@ -22,6 +24,7 @@ import {
useTemporarySavedState,
} from "#/components/TemporarySavedState/TemporarySavedState";
import { dollarsToMicros, microsToDollars } from "#/utils/currency";
import { docs } from "#/utils/docs";
import {
DefaultLimitController,
type DefaultLimitFormValues,
@@ -232,6 +235,19 @@ export const SpendPageView: FC<SpendPageViewProps> = ({
>
{(userCtrl) => (
<div className="flex max-w-[1100px] flex-col gap-8">
<Alert severity="info">
<AlertDescription>
Cost controls features will move to AI Governance in v2.37.{" "}
<Link
href={docs("/ai-coder/ai-gateway/cost-controls")}
target="_blank"
rel="noreferrer"
>
Read more here
</Link>
</AlertDescription>
</Alert>
<SettingsHeader>
<SettingsHeaderTitle>
Spend limits and usage