landing-site/src/components/Pricing.jsx

270 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react'
import { RadioGroup } from '@headlessui/react'
import clsx from 'clsx'
import { Button } from '@/components/Button'
import { Container } from '@/components/Container'
import { Logomark } from '@/components/Logo'
const plans = [
{
name: 'Starter',
featured: false,
price: { Monthly: '$0', Annually: '$0' },
description:
'Youre new to investing but want to do it right. Get started for free.',
button: {
label: 'Get started for free',
href: '/register',
},
features: [
'Commission-free trading',
'Multi-layered encryption',
'One tip every day',
'Invest up to $1,500 each month',
],
logomarkClassName: 'fill-gray-300',
},
{
name: 'Investor',
featured: false,
price: { Monthly: '$7', Annually: '$70' },
description:
'Youve been investing for a while. Invest more and grow your wealth faster.',
button: {
label: 'Subscribe',
href: '/register',
},
features: [
'Commission-free trading',
'Multi-layered encryption',
'One tip every hour',
'Invest up to $15,000 each month',
'Basic transaction anonymization',
],
logomarkClassName: 'fill-gray-500',
},
{
name: 'VIP',
featured: true,
price: { Monthly: '$199', Annually: '$1,990' },
description:
'Youve got a huge amount of assets but its not enough. To the moon.',
button: {
label: 'Subscribe',
href: '/register',
},
features: [
'Commission-free trading',
'Multi-layered encryption',
'Real-time tip notifications',
'No investment limits',
'Advanced transaction anonymization',
'Automated tax-loss harvesting',
],
logomarkClassName: 'fill-cyan-500',
},
]
function CheckIcon(props) {
return (
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
<path
d="M9.307 12.248a.75.75 0 1 0-1.114 1.004l1.114-1.004ZM11 15.25l-.557.502a.75.75 0 0 0 1.15-.043L11 15.25Zm4.844-5.041a.75.75 0 0 0-1.188-.918l1.188.918Zm-7.651 3.043 2.25 2.5 1.114-1.004-2.25-2.5-1.114 1.004Zm3.4 2.457 4.25-5.5-1.187-.918-4.25 5.5 1.188.918Z"
fill="currentColor"
/>
<circle
cx="12"
cy="12"
r="8.25"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}
function Plan({
name,
price,
description,
button,
features,
featured = false,
activePeriod,
logomarkClassName,
}) {
return (
<section
className={clsx(
'flex flex-col overflow-hidden rounded-3xl p-6 shadow-lg shadow-gray-900/5',
featured ? 'order-first bg-gray-900 lg:order-none' : 'bg-white'
)}
>
<h3
className={clsx(
'flex items-center text-sm font-semibold',
featured ? 'text-white' : 'text-gray-900'
)}
>
<Logomark className={clsx('h-6 w-6 flex-none', logomarkClassName)} />
<span className="ml-4">{name}</span>
</h3>
<p
className={clsx(
'relative mt-5 flex text-3xl tracking-tight',
featured ? 'text-white' : 'text-gray-900'
)}
>
{price.Monthly === price.Annually ? (
price.Monthly
) : (
<>
<span
aria-hidden={activePeriod === 'Annually'}
className={clsx(
'transition duration-300',
activePeriod === 'Annually' &&
'pointer-events-none translate-x-6 select-none opacity-0'
)}
>
{price.Monthly}
</span>
<span
aria-hidden={activePeriod === 'Monthly'}
className={clsx(
'absolute left-0 top-0 transition duration-300',
activePeriod === 'Monthly' &&
'pointer-events-none -translate-x-6 select-none opacity-0'
)}
>
{price.Annually}
</span>
</>
)}
</p>
<p
className={clsx(
'mt-3 text-sm',
featured ? 'text-gray-300' : 'text-gray-700'
)}
>
{description}
</p>
<div className="order-last mt-6">
<ul
role="list"
className={clsx(
'-my-2 divide-y text-sm',
featured
? 'divide-gray-800 text-gray-300'
: 'divide-gray-200 text-gray-700'
)}
>
{features.map((feature) => (
<li key={feature} className="flex py-2">
<CheckIcon
className={clsx(
'h-6 w-6 flex-none',
featured ? 'text-white' : 'text-cyan-500'
)}
/>
<span className="ml-4">{feature}</span>
</li>
))}
</ul>
</div>
<Button
href={button.href}
color={featured ? 'cyan' : 'gray'}
className="mt-6"
aria-label={`Get started with the ${name} plan for ${price}`}
>
{button.label}
</Button>
</section>
)
}
export function Pricing() {
let [activePeriod, setActivePeriod] = useState('Monthly')
return (
<section
id="pricing"
aria-labelledby="pricing-title"
className="border-t border-gray-200 bg-gray-100 py-20 sm:py-32"
>
<Container>
<div className="mx-auto max-w-2xl text-center">
<h2
id="pricing-title"
className="text-3xl font-medium tracking-tight text-gray-900"
>
Flat pricing, no management fees.
</h2>
<p className="mt-2 text-lg text-gray-600">
Whether youre one person trying to get ahead or a big firm trying
to take over the world, weve got a plan for you.
</p>
</div>
<div className="mt-8 flex justify-center">
<div className="relative">
<RadioGroup
value={activePeriod}
onChange={setActivePeriod}
className="grid grid-cols-2"
>
{['Monthly', 'Annually'].map((period) => (
<RadioGroup.Option
key={period}
value={period}
className={clsx(
'cursor-pointer border border-gray-300 py-[calc(theme(spacing.2)-1px)] px-[calc(theme(spacing.3)-1px)] text-sm text-gray-700 outline-2 outline-offset-2 transition-colors hover:border-gray-400',
period === 'Monthly'
? 'rounded-l-lg'
: '-ml-px rounded-r-lg'
)}
>
{period}
</RadioGroup.Option>
))}
</RadioGroup>
<div
aria-hidden="true"
className={clsx(
'pointer-events-none absolute inset-0 z-10 grid grid-cols-2 overflow-hidden rounded-lg bg-cyan-500 transition-all duration-300',
activePeriod === 'Monthly'
? '[clip-path:inset(0_50%_0_0)]'
: '[clip-path:inset(0_0_0_calc(50%-1px))]'
)}
>
{['Monthly', 'Annually'].map((period) => (
<div
key={period}
className={clsx(
'py-2 text-center text-sm font-semibold text-white [&:not(:focus-visible)]:focus:outline-none',
period === 'Annually' && '-ml-px'
)}
>
{period}
</div>
))}
</div>
</div>
</div>
<div className="mx-auto mt-16 grid max-w-2xl grid-cols-1 items-start gap-x-8 gap-y-10 sm:mt-20 lg:max-w-none lg:grid-cols-3">
{plans.map((plan) => (
<Plan key={plan.name} {...plan} activePeriod={activePeriod} />
))}
</div>
</Container>
</section>
)
}