mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
co-authored by
Claude Opus 4.5
parent
1b31279506
commit
57ab991a95
@@ -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",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -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 (
|
||||
|
||||
+5
-39
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user