feat(nx-dev): Migrate pricing page from nx.app (#27012)

Co-authored-by: Juri <juri.strumpflohner@gmail.com>
This commit is contained in:
Nicholas Cunningham 2024-07-25 08:14:51 -06:00 committed by GitHub
parent 6fd9cf4d45
commit a0ca85841f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 2054 additions and 27 deletions

View File

@ -0,0 +1,66 @@
import type { Metadata } from 'next';
import {
StandardPlans,
ComparablePlans,
Oss,
Faq,
} from '@nx/nx-dev/ui-pricing';
import {
Testimonials,
TrustedBy,
DefaultLayout,
CallToAction,
} from '@nx/nx-dev/ui-common';
export const metadata: Metadata = {
title: 'Nx Cloud - Available Plans',
description:
"Distribute everything, don't waste time waiting on CI. Use Nx Cloud's distributed task execution and caching features to release faster. Save time and money.",
openGraph: {
url: 'https://nx.dev/pricing',
title: 'Nx Cloud - Available Plans',
description:
"Distribute everything, don't waste time waiting on CI. Use Nx Cloud's distributed task execution and caching features to release faster. Save time and money.",
images: [
{
url: 'https://nx.dev/socials/nx-media.png',
width: 800,
height: 421,
alt: 'Nx: Smart Monorepos · Fast CI',
type: 'image/jpeg',
},
],
siteName: 'NxDev',
type: 'website',
},
};
export default function PricingPage() {
return (
<DefaultLayout>
<StandardPlans />
<div className="mt-18 lg:mt-32">
<TrustedBy utmSource="pricingpage" utmCampaign="pricing" />
</div>
<div className="mt-32 lg:mt-56">
<ComparablePlans />
</div>
<div className="mt-32 lg:mt-56">
<Testimonials />
</div>
<div className="mt-32 lg:mt-56">
<Oss />
</div>
<div className="mt-32 lg:mt-56">
<Faq />
</div>
<div className="mt-32 lg:mt-56">
<CallToAction
mainActionLinkText="Sign up"
mainActionLink="https://cloud.nx.app?utm_source=nx.dev&utm_medium=cta&utm_campaign=pricing"
mainActionTitle="Sign up to Nx Cloud"
/>
</div>
</DefaultLayout>
);
}

View File

@ -1,4 +1,4 @@
import { CallToAction, DefaultLayout } from '@nx/nx-dev/ui-common'; import { CallToAction, DefaultLayout, TrustedBy } from '@nx/nx-dev/ui-common';
import { NextSeo } from 'next-seo'; import { NextSeo } from 'next-seo';
import { import {
CiForMonorepos, CiForMonorepos,
@ -6,7 +6,6 @@ import {
SmarterToolsForMonorepos, SmarterToolsForMonorepos,
Statistics, Statistics,
TeamAndCommunity, TeamAndCommunity,
TrustedBy,
WorkBetterAchieveMoreShipQuicker, WorkBetterAchieveMoreShipQuicker,
} from '@nx/nx-dev/ui-home'; } from '@nx/nx-dev/ui-home';

View File

@ -485,6 +485,7 @@ const nxCloudUrls = {
'/core-features/:path*': '/features/:path*', '/core-features/:path*': '/features/:path*',
'/ci/recipes/set-up/connect-to-cloud': '/ci/intro/connect-to-nx-cloud', '/ci/recipes/set-up/connect-to-cloud': '/ci/intro/connect-to-nx-cloud',
'/ci/intro/connect-to-cloud': '/ci/intro/connect-to-nx-cloud', '/ci/intro/connect-to-cloud': '/ci/intro/connect-to-nx-cloud',
'/pricing/special-offer': 'https://forms.gle/FBzvsspz1o63fDAz6',
}; };
/** /**

View File

@ -19,6 +19,8 @@ export * from './lib/github-star-widget';
export * from './lib/youtube.component'; export * from './lib/youtube.component';
export * from './lib/x-icon'; export * from './lib/x-icon';
export * from './lib/discord-icon'; export * from './lib/discord-icon';
export * from './lib/trusted-by';
export * from './lib/testimonials';
export { resourceMenuItems } from './lib/headers/menu-items'; export { resourceMenuItems } from './lib/headers/menu-items';
export { solutionsMenuItems } from './lib/headers/menu-items'; export { solutionsMenuItems } from './lib/headers/menu-items';

View File

@ -1,6 +1,16 @@
import Link from 'next/link'; import Link from 'next/link';
export function CallToAction(): JSX.Element { export interface CTAProps {
mainActionLinkText?: string;
mainActionTitle?: string;
mainActionLink?: string;
}
export function CallToAction({
mainActionTitle = 'Get started with Nx',
mainActionLinkText = 'Get started',
mainActionLink = '/docs',
}: CTAProps): JSX.Element {
return ( return (
<section className="relative isolate px-6 py-32 sm:py-40 lg:px-8"> <section className="relative isolate px-6 py-32 sm:py-40 lg:px-8">
<svg <svg
@ -59,12 +69,12 @@ export function CallToAction(): JSX.Element {
</h2> </h2>
<div className="mt-10 flex items-center justify-center gap-x-6"> <div className="mt-10 flex items-center justify-center gap-x-6">
<Link <Link
href="/getting-started/intro" href={mainActionLink}
title="Get started with Nx" title={mainActionTitle}
prefetch={false} prefetch={false}
className="rounded-md bg-slate-950 px-3.5 py-2.5 text-sm font-semibold text-slate-100 shadow-sm hover:bg-slate-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white dark:bg-white dark:text-slate-900 dark:hover:bg-slate-100" className="rounded-md bg-slate-950 px-3.5 py-2.5 text-sm font-semibold text-slate-100 shadow-sm hover:bg-slate-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white dark:bg-white dark:text-slate-900 dark:hover:bg-slate-100"
> >
Get started {mainActionLinkText}
</Link> </Link>
<Link <Link
href="/contact" href="/contact"

View File

@ -1,7 +1,6 @@
'use client'; 'use client';
import { Fragment, type JSX } from 'react'; import { Fragment, type JSX } from 'react';
import { import {
ArrowUpRightIcon,
Bars3Icon, Bars3Icon,
ChevronDownIcon, ChevronDownIcon,
XMarkIcon, XMarkIcon,
@ -334,15 +333,14 @@ export function DocumentationHeader({
> >
Blog Blog
</Link> </Link>
<a <Link
href="https://nx.app/pricing" href="/pricing"
title="Nx Cloud" title="Nx Cloud"
target="_blank"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500" className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
> >
CI Pricing CI Pricing
<ArrowUpRightIcon className="h-2 w-2 align-super" /> </Link>
</a>
{/*RESOURCES*/} {/*RESOURCES*/}
<Popover className="relative"> <Popover className="relative">
{({ open }) => ( {({ open }) => (

View File

@ -1,7 +1,6 @@
'use client'; 'use client';
import { Dialog, Disclosure, Popover, Transition } from '@headlessui/react'; import { Dialog, Disclosure, Popover, Transition } from '@headlessui/react';
import { import {
ArrowUpRightIcon,
Bars4Icon, Bars4Icon,
ChevronDownIcon, ChevronDownIcon,
XMarkIcon, XMarkIcon,
@ -175,15 +174,14 @@ export function Header(): JSX.Element {
> >
Blog Blog
</Link> </Link>
<a <Link
href="https://nx.app/pricing" href="/pricing"
title="Nx Cloud" title="Nx Cloud"
target="_blank"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500" className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
> >
CI Pricing CI Pricing
<ArrowUpRightIcon className="h-2 w-2 align-super" /> </Link>
</a>
{/*RESOURCES*/} {/*RESOURCES*/}
<Popover className="relative"> <Popover className="relative">
{({ open }) => ( {({ open }) => (
@ -459,15 +457,14 @@ export function Header(): JSX.Element {
> >
Blog Blog
</Link> </Link>
<a <Link
href="https://nx.app/pricing" href="/pricing"
title="Nx Cloud" title="Nx Cloud"
target="_blank"
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500" className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
> >
CI Pricing CI Pricing
<ArrowUpRightIcon className="h-2 w-2 align-super" /> </Link>
</a>
{/*RESOURCES*/} {/*RESOURCES*/}
<Disclosure as="div"> <Disclosure as="div">
{({ open }) => ( {({ open }) => (

View File

@ -0,0 +1,58 @@
import { ChatBubbleLeftEllipsisIcon } from '@heroicons/react/24/outline';
import { SectionHeading } from './typography';
export function Testimonials(): JSX.Element {
return (
<section id="people-are-talking" className="relative">
<header className="mx-auto max-w-2xl text-center">
<SectionHeading as="h2" variant="title">
People are talking
</SectionHeading>
<SectionHeading as="p" variant="subtitle" className="mt-6">
Whether your workspace{' '}
<span className="font-semibold">
has a single project or thousands
</span>
, Nx will keep your CI fast and your workspace maintainable.
</SectionHeading>
</header>
<div className="md:px-62 mx-auto grid max-w-7xl grid-cols-1 items-stretch gap-8 px-4 py-12 md:grid-cols-3 lg:px-8 lg:py-16">
<div className="rounded-xl bg-slate-50 p-10 dark:bg-slate-800/60">
<ChatBubbleLeftEllipsisIcon className="h-8 w-8 text-blue-500 dark:text-sky-500" />
<p className="mt-4">
"It made it possible to remove our hand-rolled code and to use a
well-maintained and streamlined solution, increasing performance
while saving us a lot of time and money. The developer experience is
so good."
</p>
<div className="mt-8 font-semibold">Nitin Vericherla</div>
<div className="mt-0.5 text-sm">UI Architect at Synapse Wireless</div>
</div>
<div className="rounded-xl bg-slate-50 p-10 dark:bg-slate-800/60">
<ChatBubbleLeftEllipsisIcon className="h-8 w-8 text-blue-500 dark:text-sky-500" />
<p className="mt-4">
"By updating to the latest Lerna version and enabling Nx caching, we
were able to reduce CI build times by ~35% - a great time saving for
a fast-moving company like Sentry with an extremely active
repository."
</p>
<div className="mt-8 font-semibold">Francesco Novy</div>
<div className="mt-0.5 text-sm">
Senior Software Engineer at Sentry
</div>
</div>
<div className="rounded-xl bg-slate-50 p-10 dark:bg-slate-800/60">
<ChatBubbleLeftEllipsisIcon className="h-8 w-8 text-blue-500 dark:text-sky-500" />
<p className="mt-4">
"Since we are using NxCloud, we saw our CI times reduced by 83%!
That means our teams are not waiting hours for their PR to be merged
anymore, we reclaimed our productivity and are pretty happy about
it."
</p>
<div className="mt-8 font-semibold">Laurent Delamare</div>
<div className="mt-0.5 text-sm">Senior Engineer at VMware</div>
</div>
</div>
</section>
);
}

View File

@ -1,5 +1,5 @@
import Link from 'next/link'; import Link from 'next/link';
import { SectionDescription, SectionHeading } from '@nx/nx-dev/ui-common'; import { SectionDescription, SectionHeading } from './typography';
import { import {
AdobeIcon, AdobeIcon,
AwsIcon, AwsIcon,
@ -19,7 +19,15 @@ import {
VmwareIcon, VmwareIcon,
} from '@nx/nx-dev/ui-icons'; } from '@nx/nx-dev/ui-icons';
export function TrustedBy(): JSX.Element { export interface TrustedByProps {
utmSource?: string;
utmCampaign?: string;
}
export function TrustedBy({
utmSource = 'homepage',
utmCampaign = 'homepage_links',
}: TrustedByProps): JSX.Element {
return ( return (
<article className="mx-auto max-w-7xl px-6 lg:px-8"> <article className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="text-center"> <div className="text-center">
@ -104,9 +112,10 @@ export function TrustedBy(): JSX.Element {
className="h-10 w-10 text-slate-950 dark:text-white" className="h-10 w-10 text-slate-950 dark:text-white"
/> />
</div> </div>
<div className="mt-8 text-center"> <div className="mt-8 text-center">
<Link <Link
href="/customers?utm_source=homepage&utm_medium=website&utm_campaign=homepage_links&utm_content=our_customers" href={`/customers?utm_source=${utmSource}&utm_medium=website&utm_campaign=${utmCampaign}&utm_content=our_customers`}
title="Our customers" title="Our customers"
prefetch={false} prefetch={false}
className="group font-semibold leading-6 text-slate-950 transition-all duration-200 dark:text-white" className="group font-semibold leading-6 text-slate-950 transition-all duration-200 dark:text-white"

View File

@ -3,5 +3,4 @@ export * from './lib/hero';
export * from './lib/smarter-tools-for-monorepos'; export * from './lib/smarter-tools-for-monorepos';
export * from './lib/statistics'; export * from './lib/statistics';
export * from './lib/team-and-community'; export * from './lib/team-and-community';
export * from './lib/trusted-by';
export * from './lib/work-better-achieve-more-ship-quicker'; export * from './lib/work-better-achieve-more-ship-quicker';

View File

@ -0,0 +1,12 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}

View File

@ -0,0 +1,18 @@
{
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@ -0,0 +1,7 @@
# ui-pricing
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test ui-pricing` to execute the unit tests via [Jest](https://jestjs.io).

View File

@ -0,0 +1,9 @@
{
"name": "ui-pricing",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "nx-dev/ui-pricing/src",
"projectType": "library",
"tags": [],
"// targets": "to see all targets run: nx show project ui-pricing --web",
"targets": {}
}

View File

@ -0,0 +1,4 @@
export * from './lib/comparable-plans';
export * from './lib/faq';
export * from './lib/oss';
export * from './lib/standard-plans';

View File

@ -0,0 +1,40 @@
import { ButtonLink, SectionHeading } from '@nx/nx-dev/ui-common';
import { PlanTable } from './plans/plan-table';
export function ComparablePlans() {
return (
<section
id="plan-details"
className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"
>
<header>
<div className="mx-auto max-w-4xl text-center">
<SectionHeading as="h2" variant="title">
Plans in detail
</SectionHeading>
<SectionHeading as="p" variant="subtitle" className="mt-6">
Compare plan options and benefits.
</SectionHeading>
</div>
</header>
<div className="isolate mx-auto mt-10 max-w-full sm:mt-20">
<PlanTable />
<p className="mt-2 text-sm opacity-80">
Credits are the Nx Cloud currency allowing for usage-based pricing.
Prices do not include applicable taxes.
</p>
<div className="mt-4 flex justify-center">
<ButtonLink
href="https://cloud.nx.app"
aria-describedby="hobby-plan"
title="Hobby"
size="default"
className="my-2"
>
Get started now for free
</ButtonLink>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,150 @@
'use client';
import { Disclosure, Transition } from '@headlessui/react';
import { ChevronDownIcon } from '@heroicons/react/24/outline';
import { SectionHeading } from '@nx/nx-dev/ui-common';
import { cx } from '@nx/nx-dev/ui-primitives';
import { FAQPageJsonLd } from 'next-seo';
export function Faq(): JSX.Element {
const faqs = [
{
question: 'What are credits?',
answer:
'Credits are the currency of Nx Cloud. A determined number of credits are included in each plan. These credits are used to pay for Nx Cloud platform usage in real time.',
},
{
question: 'Do credits expire?',
answer:
'Credits expire at the end of the billing cycle and do not roll over.',
},
{
question: 'When does the billing cycle start and end?',
answer:
'A new billing cycle starts on the first day of every month. If you go over the Hobby plan limit during a cycle your organization will be disabled. You will have to upgrade to our Pro plan or wait for the next billing cycle.',
},
{
question: 'What is a CI Pipeline Execution (CIPE)?',
answer:
'By default, a CI pipeline execution is a 1:1 match to your CI provider of choice\'s concept of a "workflow".',
},
{
question: 'What is the concurrency connections limit?',
answer:
'As you scale your usage of Nx Cloud, you may run into concurrency limits. Nx Cloud puts a limit on the number of CI machines in your workspace that are simultaneously connecting to Nx Cloud. This includes any machine running in CI - both the main CI pipeline machine and any agent machines.',
},
{
question: 'What is a contributor?',
answer:
"A contributor is a person who has committed to your repository. Your organization's contributor count is calculated from anonymized, monthly git histories across all the workspaces in your Nx Cloud organization.",
},
{
question: "What if I exceed my plan's contributor limit?",
answer:
'If you exceed the contributor limit, your organization will be disabled until you upgrade to a plan that supports the number of contributors you have.',
},
{
question:
'What happens if I consume all my credits while on the Hobby plan?',
answer:
'The Hobby plan allows you to configure and run a small project at no cost. If you consume all the credits, your organization will be disabled until you upgrade to Pro or wait for the next billing cycle.',
},
{
question: 'Can I upgrade my Hobby plan to the Pro plan?',
answer:
'Yes, you can upgrade your Hobby plan to the Pro plan at any time.',
},
{
question: 'Is there a plan for open source projects?',
answer:
'Yes, we are happy to collaborate with open source projects. Please complete this form, and we will review your request and get back to you: https://nx.app/pricing/special-offer',
},
{
question: 'What payment methods do you accept?',
answer:
'We accept Visa, Mastercard, American Express, and Discover from customers worldwide.',
},
{
question: 'Do I need a credit card to create an account?',
answer:
'No, you can set up a workspace with Nx Cloud completely for free, without entering any billing information.',
},
];
return (
<section id="faq">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="lg:grid lg:grid-cols-3 lg:gap-8">
<header>
<SectionHeading as="h2" variant="title">
Not sure yet? <br /> Have questions?
</SectionHeading>
<SectionHeading as="p" variant="subtitle" className="mt-6">
Here are the most asked question we condensed for your to get you
setup quickly.
</SectionHeading>
<p className="text-md mt-4 text-slate-400 dark:text-slate-600">
Cant find the answer youre looking for? Reach out to our{' '}
<a
href="mailto:cloud-support@nrwl.io"
className="font-medium underline"
>
customer support
</a>{' '}
team.
</p>
</header>
<FAQPageJsonLd
useAppDir={true}
mainEntity={faqs.map((faq) => ({
questionName: faq.question,
acceptedAnswerText: faq.answer,
}))}
/>
<div className="mt-12 lg:col-span-2 lg:mt-0">
<dl className="mt-6 space-y-6 divide-y divide-slate-100 dark:divide-slate-800">
{faqs.map((faq) => (
<Disclosure as="div" key={faq.question} className="pt-6">
{({ open }) => (
<>
<dt className="text-lg">
<Disclosure.Button className="flex w-full items-start justify-between text-left text-slate-400">
<span className="font-medium text-slate-800 dark:text-slate-300">
{faq.question}
</span>
<span className="ml-6 flex h-7 items-center">
<ChevronDownIcon
className={cx(
open ? '-rotate-180' : 'rotate-0',
'h-6 w-6 transform transition-transform'
)}
aria-hidden="true"
/>
</span>
</Disclosure.Button>
</dt>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform -translate-y-6 opacity-0"
enterTo="transform translate-y-0 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform translate-y-0 opacity-100"
leaveTo="transform -translate-y-6 opacity-0"
>
<Disclosure.Panel as="dd" className="mt-2 pr-12">
<p className="text-base text-slate-500 dark:text-slate-400">
{faq.answer}
</p>
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>
))}
</dl>
</div>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,52 @@
import { CheckIcon } from '@heroicons/react/24/outline';
import { ButtonLink } from '@nx/nx-dev/ui-common';
export function Oss(): JSX.Element {
return (
<section
id="oss"
className="bg-blue-500 bg-gradient-to-r from-blue-500 to-cyan-500 shadow-inner"
>
<div className="px-6 py-24 sm:px-6 sm:py-32 lg:px-8">
<div className="mx-auto max-w-2xl text-center">
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
Open Source maintainers and authors?
</h2>
<p className="mx-auto mt-6 max-w-xl text-lg leading-8 text-slate-100">
We provide a <span className="font-black">$0 /month</span> plan for
open-source projects.
</p>
<div className="mt-8 flex items-center justify-center">
<ButtonLink
href="/pricing/special-offer"
aria-describedby="oss"
title="Free for Open Source"
size="default"
variant="secondary"
>
Apply for free access
</ButtonLink>
</div>
</div>
<div className="mx-auto mt-8 flex max-w-4xl flex-col gap-8 text-sm text-white md:flex-row md:justify-between">
<div className="flex items-center gap-1">
<CheckIcon className="h-6 w-5 flex-none" aria-hidden="true" /> Free
credits every month
</div>
<div className="flex items-center gap-1">
<CheckIcon className="h-6 w-5 flex-none" aria-hidden="true" />
Max 3 admin users
</div>
<div className="flex items-center gap-1">
<CheckIcon className="h-6 w-5 flex-none" aria-hidden="true" />{' '}
Powerful analytics
</div>
<div className="flex items-center gap-1">
<CheckIcon className="h-6 w-5 flex-none" aria-hidden="true" /> Basic
support
</div>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,70 @@
import { CheckIcon } from '@heroicons/react/24/outline';
import { ButtonLink } from '@nx/nx-dev/ui-common';
const features = [
'White glove onboarding',
'Work hand-in-hand with the Nx team for continual improvement',
'Run on the Nx Cloud servers in any region or run fully self-contained, on-prem',
'Premium Support and SLAs available',
'SSO / SAML Login',
];
export function EnterprisePlan({
cta = 'Learn more',
url,
}: {
cta?: string;
url: string;
}) {
return (
<article className="relative rounded-b-xl bg-slate-50/60 py-4 ring-1 ring-slate-200 xl:py-6 dark:bg-slate-800/60 dark:ring-slate-800">
<h4
id="on-prem"
className="absolute -top-9 left-0 w-full rounded-t-xl bg-gradient-to-r from-fuchsia-500 to-violet-500 p-2 text-center text-sm font-medium text-white shadow-inner ring-1 ring-slate-200 dark:ring-slate-800"
>
Available as an on-prem solution
</h4>
<header className="flex items-center justify-between gap-x-4 px-4 xl:px-6">
<h3
id="enterprise-plan"
className="text-xl font-semibold leading-8 text-slate-900 dark:text-slate-100"
>
Enterprise
</h3>
</header>
<p className="mt-4 h-12 px-4 text-sm leading-6 xl:px-6">
The ultimate Nx toolchain, tailored to your needs of speed.
</p>
<p className="mt-12 flex items-baseline gap-x-1">&nbsp;</p>
<div className="p-4 xl:p-6">
<ButtonLink
href={url}
aria-describedby="enterprise-plan"
title="Enterprise"
size="default"
variant="secondary"
className="w-full"
>
{cta}
</ButtonLink>
</div>
<p className="border-b border-t border-slate-100 bg-green-50 px-4 py-2 text-sm font-medium text-green-700 xl:px-6 dark:border-slate-800 dark:bg-green-500/10 dark:text-green-400">
Volume discounts on credits available.
</p>
<ul className="space-y-3 px-4 py-6 text-sm leading-6 text-slate-500 xl:px-6 dark:text-slate-400">
<li className="text-base font-medium text-slate-700 dark:text-slate-300">
The complete Nx toolchain tailored to your needs:
</li>
{features.map((feature) => (
<li key={feature} className="flex gap-x-3">
<CheckIcon
className="h-6 w-5 flex-none text-blue-600 dark:text-sky-600"
aria-hidden="true"
/>
{feature}
</li>
))}
</ul>
</article>
);
}

View File

@ -0,0 +1,127 @@
import { CheckIcon } from '@heroicons/react/24/outline';
import { ButtonLink } from '@nx/nx-dev/ui-common';
import Link from 'next/link';
const features = [
'50k credits included',
'Max 5 contributors',
'Distributed Task Execution (DTE)',
'Nx run detailed reports',
'Structured log visualization',
'GitHub PR with Nx Cloud live status',
'Unlimited users per workspace',
'Basic support',
];
export function HobbyPlan({
cta = 'Get started now',
url,
}: {
cta?: string;
url: string;
}) {
return (
<article className="relative rounded-b-xl py-4 ring-1 ring-blue-500 xl:py-6 dark:ring-sky-500">
<h4
id="no-credit-card-required"
className="absolute -top-9 left-0 w-full rounded-t-2xl bg-blue-500 p-2 text-center text-sm font-medium text-white shadow-inner ring-1 ring-blue-500 dark:bg-sky-500 dark:ring-sky-500"
>
Start here
</h4>
<header className="flex items-center justify-between gap-x-4 px-4 xl:px-6">
<h3
id="hobby-plan"
className="text-xl font-semibold leading-8 text-slate-900 dark:text-slate-100"
>
Hobby
</h3>
</header>
<p className="mt-4 h-12 px-4 text-sm leading-6 xl:px-6">
Get started quickly, upgrade when ready.
</p>
<p className="mt-8 flex items-baseline gap-x-1 px-4 xl:px-6">
<span className="text-4xl font-bold tracking-tight text-slate-900 dark:text-slate-100">
$0
</span>
<span className="text-base font-normal leading-6">/month</span>
</p>
<div className="p-4 xl:p-6">
<ButtonLink
href={url}
aria-describedby="hobby-plan"
title="Hobby"
size="default"
className="w-full"
>
{cta}
</ButtonLink>
</div>
<p className="border-b border-t border-slate-100 bg-green-50 px-4 py-2 text-sm font-medium text-green-700 xl:px-6 dark:border-slate-800 dark:bg-green-500/10 dark:text-green-400">
No credit card required.
</p>
<ul className="space-y-3 px-4 py-8 text-sm leading-6 text-slate-500 xl:px-6 dark:text-slate-400">
<li className="flex gap-x-3">
<CheckIcon
className="h-6 w-5 flex-none text-blue-600 dark:text-sky-600"
aria-hidden="true"
/>
<p>
<Link
href="/products/replay#content"
title="Learn more about Nx Replay"
prefetch={false}
className="font-medium text-slate-700 underline dark:text-slate-300"
>
Nx Replay
</Link>
: remote caching{' '}
</p>
</li>
<li className="flex gap-x-3">
<CheckIcon
className="h-6 w-5 flex-none text-blue-600 dark:text-sky-600"
aria-hidden="true"
/>
<p>
<Link
href="/products/agents#content"
title="Learn more about Nx Agents"
prefetch={false}
className="font-medium text-slate-700 underline dark:text-slate-300"
>
Nx Agents
</Link>
: native task distribution solution for CI{' '}
</p>
</li>
<li className="flex gap-x-3">
<CheckIcon
className="h-6 w-5 flex-none text-blue-600 dark:text-sky-600"
aria-hidden="true"
/>
<p>
<Link
href="https://nx.app/products/workflows#content?utm_source=nx.dev"
title="Learn more about Nx Workflows"
prefetch={false}
className="font-medium text-slate-700 underline dark:text-slate-300"
>
Nx Workflows
</Link>
: full CI replacement{' '}
<span className="text-xs italic">(Coming Soon)</span>
</p>
</li>
{features.map((feature) => (
<li key={feature} className="flex gap-x-3">
<CheckIcon
className="h-6 w-5 flex-none text-blue-600 dark:text-sky-600"
aria-hidden="true"
/>
{feature}
</li>
))}
</ul>
</article>
);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,99 @@
'use client';
import { Popover, Transition } from '@headlessui/react';
import { CheckIcon, InformationCircleIcon } from '@heroicons/react/24/outline';
import { ButtonLink } from '@nx/nx-dev/ui-common';
import { Fragment } from 'react';
const features = [
'300k credits included',
'Max 1m runs per month',
'Max 70 concurrent CI connections',
'Max 70 contributors',
'Unlimited private workspaces per organization',
'High-priority support',
];
export function ProPlan({
cta = 'Get started now',
url,
}: {
cta?: string;
url: string;
}) {
return (
<article className="rounded-xl py-4 ring-1 ring-slate-200 xl:py-6 dark:ring-slate-800">
<header className="flex items-center justify-between gap-x-4">
<h3
id="pro-plan"
className="px-4 text-xl font-semibold leading-8 text-slate-900 xl:px-6 dark:text-slate-100"
>
Pro
</h3>
</header>
<p className="mt-4 h-12 px-4 text-sm leading-6 xl:px-6">
Scales with your needs.
</p>
<p className="mt-8 flex items-baseline gap-x-1 px-4 xl:px-6">
<span className="text-4xl font-bold tracking-tight text-slate-900 dark:text-slate-100">
$249
</span>
<span className="text-base font-normal leading-6">/month</span>
</p>
<div className="p-4 xl:p-6">
<ButtonLink
href={url}
aria-describedby="pro-plan"
title="Pro plan"
size="default"
variant="secondary"
className="w-full"
>
{cta}
</ButtonLink>
</div>
<Popover className="relative">
<Popover.Button
as="ul"
className="cursor-pointer list-inside list-disc border-b border-t border-slate-100 bg-green-50 px-4 py-2 text-sm font-medium text-green-700 dark:border-slate-800 dark:bg-green-500/10 dark:text-green-400"
>
<li>14-day free trial</li>
<li>
No credit card required{' '}
<InformationCircleIcon className="inline h-4 w-4 align-top" />
</li>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-300"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute z-10 mt-2 w-96 overflow-hidden rounded-md border border-slate-200 bg-white p-4 text-sm shadow dark:border-slate-800 dark:bg-slate-800">
<p>
Your organization will be deactivated upon reaching any Pro plan
limit unless a credit card is set up in your account.
</p>
</Popover.Panel>
</Transition>
</Popover>
<ul className="space-y-3 px-4 py-6 text-sm leading-6 text-slate-500 xl:px-6 dark:text-slate-400">
<li className="text-base font-medium text-slate-700 dark:text-slate-300">
Everything from the Hobby plan plus:
</li>
{features.map((feature) => (
<li key={feature} className="flex gap-x-3">
<CheckIcon
className="h-6 w-5 flex-none text-blue-600 dark:text-sky-600"
aria-hidden="true"
/>
{feature}
</li>
))}
</ul>
</article>
);
}

View File

@ -0,0 +1,56 @@
import { ButtonLink, SectionHeading } from '@nx/nx-dev/ui-common';
import { EnterprisePlan } from './plans/enterprise-plan';
import { HobbyPlan } from './plans/hobby-plan';
import { ProPlan } from './plans/pro-plan';
export function StandardPlans() {
return (
<section className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<header className="mx-auto max-w-4xl text-center">
<SectionHeading as="h2" variant="display">
Start with everything,
<br /> scale when you need
</SectionHeading>
<SectionHeading as="p" variant="subtitle" className="mt-6">
Level up your CI with Nx Cloud
</SectionHeading>
</header>
<div className="relative mt-32 w-full" />
{/* Plans */}
<section id="plans">
{/* <header>
<div className="mx-auto max-w-4xl text-center">
<SectionHeading as="h2" variant="title">
Start Free, Scale Smart
</SectionHeading>
<SectionHeading as="p" variant="subtitle" className="mt-6">
Kick off with complimentary credits and explore everything Nx
Cloud offers. Add your card details when you're ready for more.
</SectionHeading>
</div>
</header> */}
<div className="isolate mx-auto mt-10 grid max-w-lg grid-cols-1 items-stretch gap-x-4 gap-y-16 sm:mt-20 md:max-w-3xl md:grid-cols-3 xl:max-w-full xl:grid-cols-3">
<HobbyPlan url="https://cloud.nx.app" />
<ProPlan url="https://cloud.nx.app" />
<EnterprisePlan url="/enterprise" />
</div>
<p className="mt-2 text-center text-sm opacity-80 xl:text-left">
Credits are the Nx Cloud currency allowing for usage based pricing.
Prices do not include applicable taxes.
</p>
</section>
<div className="mx-auto my-8 text-center">
<ButtonLink
href="#plan-details"
title="Compare all plans options and benefits"
variant="secondary"
size="default"
>
Compare all plans options and benefits
</ButtonLink>
</div>
</section>
);
}

View File

@ -0,0 +1,17 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
],
"extends": "../../tsconfig.base.json"
}

View File

@ -0,0 +1,23 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"node",
"@nx/react/typings/cssmodule.d.ts",
"@nx/react/typings/image.d.ts"
]
},
"exclude": [
"jest.config.ts",
"src/**/*.spec.ts",
"src/**/*.test.ts",
"src/**/*.spec.tsx",
"src/**/*.test.tsx",
"src/**/*.spec.js",
"src/**/*.test.js",
"src/**/*.spec.jsx",
"src/**/*.test.jsx"
],
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
}

View File

@ -107,6 +107,7 @@
"@nx/nx-dev/ui-icons": ["nx-dev/ui-icons/src/index.ts"], "@nx/nx-dev/ui-icons": ["nx-dev/ui-icons/src/index.ts"],
"@nx/nx-dev/ui-markdoc": ["nx-dev/ui-markdoc/src/index.ts"], "@nx/nx-dev/ui-markdoc": ["nx-dev/ui-markdoc/src/index.ts"],
"@nx/nx-dev/ui-member-card": ["nx-dev/ui-member-card/src/index.ts"], "@nx/nx-dev/ui-member-card": ["nx-dev/ui-member-card/src/index.ts"],
"@nx/nx-dev/ui-pricing": ["nx-dev/ui-pricing/src/index.ts"],
"@nx/nx-dev/ui-primitives": ["nx-dev/ui-primitives/src/index.ts"], "@nx/nx-dev/ui-primitives": ["nx-dev/ui-primitives/src/index.ts"],
"@nx/nx-dev/ui-references": ["nx-dev/ui-references/src/index.ts"], "@nx/nx-dev/ui-references": ["nx-dev/ui-references/src/index.ts"],
"@nx/nx-dev/ui-sponsor-card": ["nx-dev/ui-sponsor-card/src/index.ts"], "@nx/nx-dev/ui-sponsor-card": ["nx-dev/ui-sponsor-card/src/index.ts"],