chore: update paywall to mention AI governance-add on (#21659)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ben Potter
2026-01-28 17:37:15 -06:00
committed by GitHub
co-authored by Claude Opus 4.5
parent 1b31279506
commit 57ab991a95
6 changed files with 108 additions and 88 deletions
+8 -2
View File
@@ -137,7 +137,13 @@ export const EnterpriseBadge: FC = () => {
);
};
export const PremiumBadge: FC = () => {
interface PremiumBadgeProps {
children?: React.ReactNode;
}
export const PremiumBadge: FC<PremiumBadgeProps> = ({
children = "Premium",
}) => {
return (
<span
css={[
@@ -149,7 +155,7 @@ export const PremiumBadge: FC = () => {
}),
]}
>
Premium
{children}
</span>
);
};
@@ -0,0 +1,27 @@
import type { ComponentProps } from "react";
import type { Paywall } from "./Paywall";
// Centralized AI Bridge paywall configuration to avoid duplication.
export const aiBridgePaywallConfig: Omit<ComponentProps<typeof Paywall>, ""> = {
message: "AI Bridge",
description:
"AI Bridge provides auditable visibility into user prompts and LLM tool calls from developer tools within Coder Workspaces. AI Bridge requires a Premium license with AI Governance add-on.",
documentationLink: "https://coder.com/docs/ai-coder/ai-governance",
documentationLinkText: "Learn about AI Governance",
badgeText: "AI Governance",
ctaText: "Contact Sales",
ctaLink: "https://coder.com/contact",
features: [
{ text: "Auditable history of user prompts" },
{ text: "Logged LLM tool invocations" },
{ text: "Token usage and consumption visibility" },
{ text: "Centrally-managed MCP servers" },
{
text: "Visit",
link: {
href: "https://coder.com/docs/ai-coder/ai-bridge",
text: "AI Bridge Docs",
},
},
],
};
+57 -32
View File
@@ -10,61 +10,86 @@ interface PaywallProps {
message: string;
description?: ReactNode;
documentationLink?: string;
documentationLinkText?: string;
features?: Array<{
text: string;
link?: { href: string; text: string };
}>;
badgeText?: string;
ctaText?: string;
ctaLink?: string;
}
export const Paywall: FC<PaywallProps> = ({
message,
description,
documentationLink,
documentationLinkText = "Read the documentation",
features,
badgeText = "Premium",
ctaText = "Learn about Premium",
ctaLink = "https://coder.com/pricing#compare-plans",
}) => {
const defaultFeatures: Array<{
text: string;
link?: { href: string; text: string };
}> = [
{ text: "High availability & workspace proxies" },
{ text: "Multi-org & role-based access control" },
{ text: "24x7 global support with SLA" },
{ text: "Unlimited Git & external auth integrations" },
];
const displayFeatures = features ?? defaultFeatures;
return (
<div css={styles.root}>
<div>
<Stack direction="row" alignItems="center" className="mb-6">
<h5 className="font-semibold font-inherit text-xl m-0">{message}</h5>
<PremiumBadge />
<PremiumBadge>{badgeText}</PremiumBadge>
</Stack>
{description && (
<p className="font-inherit max-w-md text-sm">{description}</p>
<p className="font-inherit max-w-md text-sm mb-4">{description}</p>
)}
{documentationLink && (
<Link
href={documentationLink}
target="_blank"
rel="noreferrer"
className="font-semibold"
>
{documentationLinkText}
</Link>
)}
<Link
href={documentationLink}
target="_blank"
rel="noreferrer"
className="font-semibold"
>
Read the documentation
</Link>
</div>
<div className="w-px h-[220px] bg-highlight-purple/50 ml-2" />
<Stack direction="column" alignItems="left" spacing={3}>
<ul className="m-0 px-6 text-sm font-medium">
<li css={styles.feature}>
<FeatureIcon />
High availability & workspace proxies
</li>
<li css={styles.feature}>
<FeatureIcon />
Multi-org & role-based access control
</li>
<li css={styles.feature}>
<FeatureIcon />
24x7 global support with SLA
</li>
<li css={styles.feature}>
<FeatureIcon />
Unlimited Git & external auth integrations
</li>
{displayFeatures.map((feature, index) => (
<li key={index} css={styles.feature}>
<FeatureIcon />
{feature.text}
{feature.link && (
<>
{" "}
<Link
href={feature.link.href}
target="_blank"
rel="noreferrer"
className="font-semibold text-sm"
>
{feature.link.text}
</Link>
</>
)}
</li>
))}
</ul>
<div className="px-7">
<Button asChild>
<a
href="https://coder.com/pricing#compare-plans"
target="_blank"
rel="noreferrer"
>
Learn about Premium
<a href={ctaLink} target="_blank" rel="noreferrer">
{ctaText}
</a>
</Button>
</div>
@@ -10,12 +10,18 @@ interface PopoverPaywallProps {
message: string;
description?: ReactNode;
documentationLink?: string;
badgeText?: string;
ctaText?: string;
ctaLink?: string;
}
export const PopoverPaywall: FC<PopoverPaywallProps> = ({
message,
description,
documentationLink,
badgeText = "Premium",
ctaText = "Learn about Premium",
ctaLink = "https://coder.com/pricing#compare-plans",
}) => {
return (
<div
@@ -30,7 +36,7 @@ export const PopoverPaywall: FC<PopoverPaywallProps> = ({
<div>
<Stack direction="row" alignItems="center" css={{ marginBottom: 18 }}>
<h5 css={styles.title}>{message}</h5>
<PremiumBadge />
<PremiumBadge>{badgeText}</PremiumBadge>
</Stack>
{description && <p css={styles.description}>{description}</p>}
@@ -61,12 +67,8 @@ export const PopoverPaywall: FC<PopoverPaywallProps> = ({
</ul>
<div css={styles.learnButton}>
<Button asChild>
<a
href="https://coder.com/pricing#compare-plans"
target="_blank"
rel="noreferrer"
>
Learn about Premium
<a href={ctaLink} target="_blank" rel="noreferrer">
{ctaText}
</a>
</Button>
</div>
@@ -3,6 +3,7 @@ import {
PaginationContainer,
type PaginationResult,
} from "components/PaginationWidget/PaginationContainer";
import { aiBridgePaywallConfig } from "components/Paywall/AIBridgePaywallConfig";
import { Paywall } from "components/Paywall/Paywall";
import {
Table,
@@ -14,7 +15,6 @@ import {
import { TableEmpty } from "components/TableEmpty/TableEmpty";
import { TableLoader } from "components/TableLoader/TableLoader";
import type { ComponentProps, FC } from "react";
import { docs } from "utils/docs";
import { RequestLogsFilter } from "./filter/RequestLogsFilter";
import { RequestLogsRow } from "./RequestLogsRow/RequestLogsRow";
@@ -34,13 +34,7 @@ export const RequestLogsPageView: FC<RequestLogsPageViewProps> = ({
filterProps,
}) => {
if (!isRequestLogsVisible) {
return (
<Paywall
message="AI Bridge"
description="AI Bridge allows you to monitor and manage AI requests. You need an Premium license to use this feature."
documentationLink={docs("/ai-coder/ai-bridge")}
/>
);
return <Paywall {...aiBridgePaywallConfig} />;
}
return (
@@ -1,7 +1,6 @@
import type { SerpentOption } from "api/typesGenerated";
import { Badges, PremiumBadge } from "components/Badges/Badges";
import { aiBridgePaywallConfig } from "components/Paywall/AIBridgePaywallConfig";
import { Paywall } from "components/Paywall/Paywall";
import { PopoverPaywall } from "components/Paywall/PopoverPaywall";
import {
SettingsHeader,
SettingsHeaderDescription,
@@ -9,11 +8,6 @@ import {
SettingsHeaderTitle,
} from "components/SettingsHeader/SettingsHeader";
import { Stack } from "components/Stack/Stack";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type { FC } from "react";
import { deploymentGroupHasParent } from "utils/deployOptions";
import { docs } from "utils/docs";
@@ -29,33 +23,9 @@ export const AIGovernanceSettingsPageView: FC<
> = ({ options, featureAIBridgeEnabled }) => {
return (
<Stack direction="column" spacing={6}>
<div>
<SettingsHeader>
<SettingsHeaderTitle>AI Governance</SettingsHeaderTitle>
</SettingsHeader>
<Badges>
<Tooltip>
<TooltipTrigger asChild>
<span>
<PremiumBadge />
</span>
</TooltipTrigger>
<TooltipContent
sideOffset={-28}
collisionPadding={16}
className="p-0"
>
<PopoverPaywall
message="AI Governance"
description="With a Premium license, you can monitor and manage AI requests across your deployment."
documentationLink={docs("/ai-coder/ai-governance")}
/>
</TooltipContent>
</Tooltip>
</Badges>
</div>
<SettingsHeader>
<SettingsHeaderTitle>AI Governance</SettingsHeaderTitle>
</SettingsHeader>
{featureAIBridgeEnabled ? (
<div>
@@ -79,11 +49,7 @@ export const AIGovernanceSettingsPageView: FC<
/>
</div>
) : (
<Paywall
message="AI Bridge"
description="AI Bridge is a smart gateway for AI that intercepts traffic between coding agents and AI providers. Enable it to monitor prompts, token usage, and tool invocations across your deployment."
documentationLink={docs("/ai-coder/ai-bridge")}
/>
<Paywall {...aiBridgePaywallConfig} />
)}
</Stack>
);