diff --git a/site/src/components/Badges/Badges.tsx b/site/src/components/Badges/Badges.tsx index 07b653e7f4..90ae6dc506 100644 --- a/site/src/components/Badges/Badges.tsx +++ b/site/src/components/Badges/Badges.tsx @@ -1,4 +1,5 @@ import type { Interpolation, Theme } from "@emotion/react"; +import { Badge } from "components/Badge/Badge"; import { Stack } from "components/Stack/Stack"; import { Tooltip, @@ -11,6 +12,7 @@ import { type HTMLAttributes, type PropsWithChildren, } from "react"; +import { cn } from "utils/cn"; const styles = { badge: { @@ -145,18 +147,15 @@ export const PremiumBadge: FC = ({ children = "Premium", }) => { return ( - ({ - backgroundColor: theme.branding.premium.background, - border: `1px solid ${theme.branding.premium.border}`, - color: theme.branding.premium.text, - }), - ]} + {children} - + ); }; diff --git a/site/src/components/Paywall/AIBridgePaywallConfig.ts b/site/src/components/Paywall/AIBridgePaywallConfig.ts deleted file mode 100644 index f7641453f2..0000000000 --- a/site/src/components/Paywall/AIBridgePaywallConfig.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { ComponentProps } from "react"; -import type { Paywall } from "./Paywall"; - -// Centralized AI Bridge paywall configuration to avoid duplication. -export const aiBridgePaywallConfig: Omit, ""> = { - 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", - }, - }, - ], -}; diff --git a/site/src/components/Paywall/Paywall.stories.tsx b/site/src/components/Paywall/Paywall.stories.tsx index d5ff40d854..ba2e4dcce5 100644 --- a/site/src/components/Paywall/Paywall.stories.tsx +++ b/site/src/components/Paywall/Paywall.stories.tsx @@ -1,5 +1,17 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Paywall } from "./Paywall"; +import { PremiumBadge } from "components/Badges/Badges"; +import { + Paywall, + PaywallContent, + PaywallCTA, + PaywallDescription, + PaywallFeature, + PaywallFeatures, + PaywallHeading, + PaywallSeparator, + PaywallStack, + PaywallTitle, +} from "./Paywall"; const meta: Meta = { title: "components/Paywall", @@ -9,10 +21,40 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Premium: Story = { +export const Default: Story = { args: { - message: "Black Lotus", - description: - "Adds 3 mana of any single color of your choice to your mana pool, then is discarded. Tapping this artifact can be played as an interrupt.", + children: ( + <> + + + Black Lotus + + + + Adds 3 mana of any single color of your choice to your mana pool, + then is discarded. Tapping this artifact can be played as an + interrupt. + + + + + + + High availability & workspace proxies + + + Multi-org & role-based access control + + 24x7 global support with SLA + + Unlimited Git & external auth integrations + + + + Learn about Premium + + + + ), }, }; diff --git a/site/src/components/Paywall/Paywall.tsx b/site/src/components/Paywall/Paywall.tsx index 5236697240..81842c7942 100644 --- a/site/src/components/Paywall/Paywall.tsx +++ b/site/src/components/Paywall/Paywall.tsx @@ -1,134 +1,192 @@ -import type { Interpolation, Theme } from "@emotion/react"; -import Link from "@mui/material/Link"; -import { PremiumBadge } from "components/Badges/Badges"; import { Button } from "components/Button/Button"; -import { Stack } from "components/Stack/Stack"; import { CircleCheckBigIcon } from "lucide-react"; -import type { FC, ReactNode } from "react"; +import type React from "react"; +import type { FC } from "react"; +import { cn } from "utils/cn"; -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 = ({ - 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; +export const Paywall = ({ + className, + children, + ...props +}: React.ComponentProps<"div">) => { return ( -
-
- -
{message}
- {badgeText} -
- - {description && ( -

{description}

- )} - {documentationLink && ( - - {documentationLinkText} - - )} -
-
- -
    - {displayFeatures.map((feature, index) => ( -
  • - - {feature.text} - {feature.link && ( - <> - {" "} - - {feature.link.text} - - - )} -
  • - ))} -
- -
+
+ {children}
); }; -const FeatureIcon: FC = () => { +export const PaywallContent: FC> = ({ + children, + ...props +}) => { + return
{children}
; +}; + +export const PaywallHeading: FC> = ({ + children, + className, + ...props +}) => { return ( -