mirror of
https://github.com/lukevella/rallly.git
synced 2025-08-03 16:38:34 +02:00
📝 Update pricing page (#884)
This commit is contained in:
parent
a089eba82b
commit
029ab48df5
6 changed files with 344 additions and 343 deletions
|
@ -1,29 +1,30 @@
|
|||
{
|
||||
"pricing": "Pricing",
|
||||
"pricingDescription": "Get started for free. No login required.",
|
||||
"annualBilling": "Annual billing (Save {discount}%)",
|
||||
"planFree": "Free",
|
||||
"freeForever": "free forever",
|
||||
"unlimitedPolls": "Unlimited polls",
|
||||
"unlimitedParticipants": "Unlimited participants",
|
||||
"getStarted": "Get started for free",
|
||||
"planPro": "Pro",
|
||||
"annualBillingDescription": "per month, billed annually",
|
||||
"monthlyBillingDescription": "per month",
|
||||
"finalizePolls": "Finalize polls",
|
||||
"extendedPollLife": "Extended poll life",
|
||||
"prioritySupport": "Priority support",
|
||||
"upgrade": "Upgrade",
|
||||
"priceIncreaseInfo": "Prices will be adjusted regularly as more features are added",
|
||||
"faq": "Frequently Asked Questions",
|
||||
"canUseFree": "Can I use Rallly for free?",
|
||||
"canUseFreeAnswer": "Yes, as a free user you can create polls and get insight into your participant's availability. You will still see the results of your poll but you won't be able to select a final date or send calendar invites.",
|
||||
"whyUpgrade": "Why should I upgrade?",
|
||||
"whyUpgradeAnswer": "When you upgrade to a paid plan, you will be able to finalize your polls and automatically send calendar invites to your participants with your selected date. We will also keep your polls indefinitely so they won't be automatically deleted even after they are finalized.",
|
||||
"howToUpgrade": "How do I upgrade to a paid plan?",
|
||||
"howToUpgradeAnswer": "To upgrade, you can go to your <a>billing settings</a> and click on <b>Upgrade</b>.",
|
||||
"cancelSubscription": "How do I cancel my subscription?",
|
||||
"cancelSubscriptionAnswer": "You can cancel your subscription at any time by going to your <a>billing settings</a>. Once you cancel your subscription, you will still have access to your paid plan until the end of your billing period. After that, you will be downgraded to a free plan.",
|
||||
"earlyAccess": "Early bird discount",
|
||||
"customPollSettings": "Customizable poll settings"
|
||||
"billingPeriodMonthly": "Monthly",
|
||||
"billingPeriodYearly": "Yearly",
|
||||
"planFreeDescription": "For casual users",
|
||||
"limitedAccess": "Access to core features",
|
||||
"pollsDeleted": "Polls are automatically deleted once they become inactive",
|
||||
"planProDescription": "For power users and professionals",
|
||||
"accessAllFeatures": "Access all features",
|
||||
"getEarlyAccess": "Get early access to new features",
|
||||
"canUseFreeAnswer2": "Yes, most of Rallly's features are free and many users will never need to pay for anything. However, there are some features that are only available to paying customers. These features are designed to help you get the most out of Rallly.",
|
||||
"whyUpgradeAnswer2": "Upgrading to a paid plan makes sense if you use Rallly often or use it for work. The current subscription rate is a special early adopter rate and will increase in the future. By upgrading now, you will get early access to new, high-quality scheduling tools as they are released and lock in your subscription rate so you won't be affected by future price increases.",
|
||||
"upgradeNowSaveLater": "Upgrade now, save later",
|
||||
"earlyAdopterDescription": "As an early adopter, you'll lock in your subscription rate and won't be affected by future price increases.",
|
||||
"planFree": "Free",
|
||||
"keepPollsIndefinitely": "Keep polls indefinitely"
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ const Footer: React.FunctionComponent = () => {
|
|||
height={30}
|
||||
alt="Rallly"
|
||||
/>
|
||||
<div className="my-8 text-gray-500">
|
||||
<div className="my-8 text-sm text-gray-500">
|
||||
<p className="mb-4 leading-relaxed">
|
||||
<Trans
|
||||
i18nKey="footerSponsor"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { InfoIcon } from "@rallly/icons";
|
||||
import { Badge } from "@rallly/ui/badge";
|
||||
import { TrendingUpIcon } from "@rallly/icons";
|
||||
import {
|
||||
BillingPlan,
|
||||
BillingPlanFooter,
|
||||
BillingPlanDescription,
|
||||
BillingPlanHeader,
|
||||
BillingPlanPeriod,
|
||||
BillingPlanPerk,
|
||||
|
@ -11,8 +10,7 @@ import {
|
|||
BillingPlanTitle,
|
||||
} from "@rallly/ui/billing-plan";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { Label } from "@rallly/ui/label";
|
||||
import { Switch } from "@rallly/ui/switch";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/tabs";
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { NextSeo } from "next-seo";
|
||||
|
@ -27,13 +25,245 @@ import { getStaticTranslations } from "@/utils/page-translations";
|
|||
const monthlyPriceUsd = 7;
|
||||
const annualPriceUsd = 42;
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
const { t } = useTranslation();
|
||||
const [annualBilling, setAnnualBilling] = React.useState(true);
|
||||
export const UpgradeButton = ({
|
||||
children,
|
||||
annual,
|
||||
}: React.PropsWithChildren<{ annual?: boolean }>) => {
|
||||
return (
|
||||
<div className="mx-auto bg-gray-100">
|
||||
<NextSeo title={t("common:pricing", { defaultValue: "Pricing" })} />
|
||||
<div className="text-center">
|
||||
<form method="POST" action={linkToApp("/api/stripe/checkout")}>
|
||||
<input
|
||||
type="hidden"
|
||||
name="period"
|
||||
value={annual ? "yearly" : "monthly"}
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="return_path"
|
||||
value={window.location.pathname}
|
||||
/>
|
||||
<Button className="w-full" type="submit" variant="primary">
|
||||
{children || <Trans i18nKey="pricing:upgrade" defaults="Upgrade" />}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const PriceTables = () => {
|
||||
const [tab, setTab] = React.useState("yearly");
|
||||
return (
|
||||
<Tabs value={tab} onValueChange={setTab}>
|
||||
<div className="flex justify-center">
|
||||
<TabsList className="mb-4 sm:mb-6">
|
||||
<TabsTrigger value="monthly">
|
||||
<Trans i18nKey="pricing:billingPeriodMonthly" defaults="Monthly" />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="yearly">
|
||||
<Trans i18nKey="pricing:billingPeriodYearly" defaults="Yearly" />
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
<div className="mx-auto grid gap-4 sm:gap-6 md:grid-cols-2">
|
||||
<BillingPlan>
|
||||
<BillingPlanHeader>
|
||||
<BillingPlanTitle>
|
||||
<Trans i18nKey="pricing:planFree" defaults="Free" />
|
||||
</BillingPlanTitle>
|
||||
<BillingPlanDescription>
|
||||
<Trans
|
||||
i18nKey="pricing:planFreeDescription"
|
||||
defaults="For casual users"
|
||||
/>
|
||||
</BillingPlanDescription>
|
||||
</BillingPlanHeader>
|
||||
<div>
|
||||
<BillingPlanPrice>$0</BillingPlanPrice>
|
||||
<BillingPlanPeriod>
|
||||
<Trans i18nKey="pricing:freeForever" defaults="free forever" />
|
||||
</BillingPlanPeriod>
|
||||
</div>
|
||||
<hr />
|
||||
<Button className="w-full">
|
||||
<Link href={linkToApp("/")}>
|
||||
<Trans i18nKey="common:getStarted" defaults="Get started" />
|
||||
</Link>
|
||||
</Button>
|
||||
<BillingPlanPerks>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:limitedAccess"
|
||||
defaults="Access to core features"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:pollsDeleted"
|
||||
defaults="Polls are automatically deleted once they become inactive"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
</BillingPlanPerks>
|
||||
</BillingPlan>
|
||||
<BillingPlan className="relative">
|
||||
<BillingPlanHeader>
|
||||
<BillingPlanTitle>
|
||||
<Trans i18nKey="pricing:planPro" defaults="Pro" />
|
||||
</BillingPlanTitle>
|
||||
<BillingPlanDescription>
|
||||
<Trans
|
||||
i18nKey="pricing:planProDescription"
|
||||
defaults="For power users and professionals"
|
||||
/>
|
||||
</BillingPlanDescription>
|
||||
</BillingPlanHeader>
|
||||
<TabsContent value="yearly">
|
||||
<BillingPlanPrice discount={`$${(annualPriceUsd / 12).toFixed(2)}`}>
|
||||
${monthlyPriceUsd}
|
||||
</BillingPlanPrice>
|
||||
<BillingPlanPeriod>
|
||||
<Trans
|
||||
i18nKey="pricing:annualBillingDescription"
|
||||
defaults="per month, billed annually"
|
||||
/>
|
||||
</BillingPlanPeriod>
|
||||
</TabsContent>
|
||||
<TabsContent value="monthly">
|
||||
<BillingPlanPrice>${monthlyPriceUsd}</BillingPlanPrice>
|
||||
<BillingPlanPeriod>
|
||||
<Trans
|
||||
i18nKey="pricing:monthlyBillingDescription"
|
||||
defaults="per month"
|
||||
/>
|
||||
</BillingPlanPeriod>
|
||||
</TabsContent>
|
||||
<hr />
|
||||
<Button asChild variant="primary" className="w-full">
|
||||
<Link href={linkToApp("/settings/billing")}>
|
||||
<Trans i18nKey="pricing:upgrade" defaults="Go to billing" />
|
||||
</Link>
|
||||
</Button>
|
||||
<BillingPlanPerks>
|
||||
<BillingPlanPerk pro={true}>
|
||||
<Trans
|
||||
i18nKey="pricing:accessAllFeatures"
|
||||
defaults="Access all features"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk pro={true}>
|
||||
<Trans
|
||||
i18nKey="pricing:keepPollsIndefinitely"
|
||||
defaults="Keep polls indefinitely"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk pro={true}>
|
||||
<Trans
|
||||
i18nKey="pricing:getEarlyAccess"
|
||||
defaults="Get early access to new features"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
</BillingPlanPerks>
|
||||
</BillingPlan>
|
||||
</div>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
const FAQ = () => {
|
||||
return (
|
||||
<div className="rounded-md p-6">
|
||||
<h2 className="mb-4 sm:mb-6">
|
||||
<Trans
|
||||
i18nKey="pricing:faq"
|
||||
defaults="Frequently Asked Questions"
|
||||
></Trans>
|
||||
</h2>
|
||||
<div className="space-y-4 sm:space-y-6">
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-2">
|
||||
<h3 className="col-span-1">
|
||||
<Trans
|
||||
i18nKey="pricing:canUseFree"
|
||||
defaults="Can I use Rallly for free?"
|
||||
></Trans>
|
||||
</h3>
|
||||
<p className="col-span-2 text-sm leading-relaxed text-slate-600">
|
||||
<Trans
|
||||
i18nKey="pricing:canUseFreeAnswer2"
|
||||
defaults="Yes, most of Rallly's features are free and many users will never need to pay for anything. However, there are some features that are only available to paying customers. These features are designed to help you get the most out of Rallly."
|
||||
></Trans>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-2">
|
||||
<h3 className="col-span-1">
|
||||
<Trans
|
||||
i18nKey="pricing:whyUpgrade"
|
||||
defaults="Why should I upgrade?"
|
||||
></Trans>
|
||||
</h3>
|
||||
<p className="col-span-2 text-sm leading-relaxed text-slate-600">
|
||||
<Trans
|
||||
i18nKey="pricing:whyUpgradeAnswer2"
|
||||
defaults="Upgrading to a paid plan makes sense if you use Rallly often or use it for work. The current subscription rate is a special early adopter rate and will increase in the future. By upgrading now, you will get early access to new, high-quality scheduling tools as they are released and lock in your subscription rate so you won't be affected by future price increases."
|
||||
></Trans>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-2">
|
||||
<h3 className="col-span-1">
|
||||
<Trans
|
||||
i18nKey="pricing:howToUpgrade"
|
||||
defaults="How do I upgrade to a paid plan?"
|
||||
/>
|
||||
</h3>
|
||||
<p className="col-span-2 text-sm leading-relaxed text-slate-600">
|
||||
<Trans
|
||||
i18nKey="pricing:howToUpgradeAnswer"
|
||||
components={{
|
||||
a: (
|
||||
<Link
|
||||
className="text-link"
|
||||
href={linkToApp("/settings/billing")}
|
||||
/>
|
||||
),
|
||||
b: <strong />,
|
||||
}}
|
||||
defaults="To upgrade, you can go to your <a>billing settings</a> and click on <b>Upgrade</b>."
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-2">
|
||||
<h3 className="col-span-1">
|
||||
<Trans
|
||||
i18nKey="pricing:cancelSubscription"
|
||||
defaults="How do I cancel my subscription?"
|
||||
></Trans>
|
||||
</h3>
|
||||
<p className="col-span-2 text-sm leading-relaxed text-slate-600">
|
||||
<Trans
|
||||
i18nKey="pricing:cancelSubscriptionAnswer"
|
||||
components={{
|
||||
a: (
|
||||
<Link
|
||||
className="text-link"
|
||||
href={linkToApp("/settings/billing")}
|
||||
/>
|
||||
),
|
||||
b: <strong />,
|
||||
}}
|
||||
defaults="You can cancel your subscription at any time by going to your <a>billing settings</a>. Once you cancel your subscription, you will still have access to your paid plan until the end of your billing period. After that, you will be downgraded to a free plan."
|
||||
></Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
const { t } = useTranslation(["pricing"]);
|
||||
return (
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<NextSeo
|
||||
title={t("common:pricing", { defaultValue: "Pricing" })}
|
||||
description={t("pricing:pricingDescription")}
|
||||
/>
|
||||
<div className="mb-4 text-center sm:mb-6">
|
||||
<h1 className="mb-4 text-4xl font-bold tracking-tight">
|
||||
<Trans i18nKey="pricing:pricing">Pricing</Trans>
|
||||
</h1>
|
||||
|
@ -44,242 +274,29 @@ const Page: NextPageWithLayout = () => {
|
|||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div className="mx-auto my-8 max-w-2xl">
|
||||
<div className="mb-8 flex items-center justify-center gap-x-2">
|
||||
<Switch
|
||||
id="annual-billing"
|
||||
checked={annualBilling}
|
||||
onCheckedChange={setAnnualBilling}
|
||||
/>
|
||||
<Label htmlFor="annual-billing">
|
||||
<div className="space-y-4 sm:space-y-6">
|
||||
<PriceTables />
|
||||
<div className="rounded-md border bg-gradient-to-b from-cyan-50 to-cyan-50/60 px-5 py-4 text-cyan-800">
|
||||
<div className="mb-2">
|
||||
<TrendingUpIcon className="text-indigo mr-2 mt-0.5 h-6 w-6 shrink-0" />
|
||||
</div>
|
||||
<div className="mb-1 flex items-center gap-x-2">
|
||||
<h3 className="text-sm">
|
||||
<Trans
|
||||
i18nKey="pricing:upgradeNowSaveLater"
|
||||
defaults="Upgrade now, save later"
|
||||
/>
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-sm">
|
||||
<Trans
|
||||
i18nKey="pricing:annualBilling"
|
||||
values={{ discount: 50 }}
|
||||
defaults="Annual billing (Save {discount}%)"
|
||||
i18nKey="pricing:earlyAdopterDescription"
|
||||
defaults="As an early adopter, you'll lock in your subscription rate and won't be affected by future price increases."
|
||||
/>
|
||||
</Label>
|
||||
</div>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<BillingPlan>
|
||||
<BillingPlanHeader>
|
||||
<BillingPlanTitle>
|
||||
<Trans i18nKey="pricing:planFree" defaults="Free" />
|
||||
</BillingPlanTitle>
|
||||
<BillingPlanPrice>$0</BillingPlanPrice>
|
||||
<BillingPlanPeriod>
|
||||
<Trans i18nKey="pricing:freeForever" defaults="free forever" />
|
||||
</BillingPlanPeriod>
|
||||
</BillingPlanHeader>
|
||||
<BillingPlanPerks>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:unlimitedPolls"
|
||||
defaults="Unlimited polls"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:unlimitedParticipants"
|
||||
defaults="Unlimited participants"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
</BillingPlanPerks>
|
||||
<BillingPlanFooter>
|
||||
<Button className="w-full" asChild>
|
||||
<Link href={linkToApp()}>
|
||||
<Trans i18nKey="pricing:getStarted">
|
||||
Get started for free
|
||||
</Trans>
|
||||
</Link>
|
||||
</Button>
|
||||
</BillingPlanFooter>
|
||||
</BillingPlan>
|
||||
<BillingPlan variant="primary">
|
||||
<BillingPlanHeader>
|
||||
<div className="flex justify-between">
|
||||
<BillingPlanTitle className="text-primary m-0">
|
||||
<Trans i18nKey="pricing:planPro" defaults="Pro" />
|
||||
</BillingPlanTitle>
|
||||
<Badge variant="secondary">
|
||||
<Trans
|
||||
i18nKey="pricing:earlyAccess"
|
||||
defaults="Early bird discount"
|
||||
/>
|
||||
</Badge>
|
||||
</div>
|
||||
{annualBilling ? (
|
||||
<>
|
||||
<BillingPlanPrice
|
||||
discount={`$${(annualPriceUsd / 12).toFixed(2)}`}
|
||||
>
|
||||
${monthlyPriceUsd}
|
||||
</BillingPlanPrice>
|
||||
<BillingPlanPeriod>
|
||||
<Trans
|
||||
i18nKey="pricing:annualBillingDescription"
|
||||
defaults="per month, billed annually"
|
||||
/>
|
||||
</BillingPlanPeriod>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<BillingPlanPrice>${monthlyPriceUsd}</BillingPlanPrice>
|
||||
<BillingPlanPeriod>
|
||||
<Trans
|
||||
i18nKey="pricing:monthlyBillingDescription"
|
||||
defaults="per month"
|
||||
/>
|
||||
</BillingPlanPeriod>
|
||||
</>
|
||||
)}
|
||||
</BillingPlanHeader>
|
||||
<BillingPlanPerks>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:unlimitedPolls"
|
||||
defaults="Unlimited polls"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:unlimitedParticipants"
|
||||
defaults="Unlimited participants"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:customPollSettings"
|
||||
defaults="Customizable poll settings"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:finalizePolls"
|
||||
defaults="Finalize polls"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:extendedPollLife"
|
||||
defaults="Extended poll life"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pricing:prioritySupport"
|
||||
defaults="Priority support"
|
||||
/>
|
||||
</BillingPlanPerk>
|
||||
</BillingPlanPerks>
|
||||
<BillingPlanFooter>
|
||||
<Button variant="primary" className="w-full" asChild>
|
||||
<Link href={linkToApp("/settings/billing")}>
|
||||
<Trans i18nKey="pricing:upgrade">Upgrade</Trans>
|
||||
</Link>
|
||||
</Button>
|
||||
</BillingPlanFooter>
|
||||
</BillingPlan>
|
||||
</div>
|
||||
<p className="text-muted-foreground mt-8 flex gap-x-2 text-sm sm:justify-center sm:text-center">
|
||||
<InfoIcon className="h-4 w-4 shrink-0 translate-y-0.5" />
|
||||
<Trans i18nKey="pricing:priceIncreaseInfo">
|
||||
Prices will be adjusted regularly as more features are added
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
<hr className="my-8" />
|
||||
<div>
|
||||
<h2 className="mb-4">
|
||||
<Trans
|
||||
i18nKey="pricing:faq"
|
||||
defaults="Frequently Asked Questions"
|
||||
></Trans>
|
||||
</h2>
|
||||
<div className="divide-y">
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-2 py-4 md:grid-cols-3">
|
||||
<h3 className="col-span-1">
|
||||
<Trans
|
||||
i18nKey="pricing:canUseFree"
|
||||
defaults="Can I use Rallly for free?"
|
||||
></Trans>
|
||||
</h3>
|
||||
<p className="col-span-2 text-sm leading-relaxed text-slate-600">
|
||||
<Trans
|
||||
i18nKey="pricing:canUseFreeAnswer"
|
||||
components={{
|
||||
a: (
|
||||
<Link
|
||||
className="text-link"
|
||||
href={linkToApp("settings/billing")}
|
||||
/>
|
||||
),
|
||||
b: <strong />,
|
||||
}}
|
||||
defaults="Yes, as a free user you can create polls and get insight into your participant's availability. You will still see the results of your poll but you won't be able to select a final date or send calendar invites."
|
||||
></Trans>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-2 py-4 md:grid-cols-3">
|
||||
<h3 className="col-span-1">
|
||||
<Trans
|
||||
i18nKey="pricing:whyUpgrade"
|
||||
defaults="Why should I upgrade?"
|
||||
></Trans>
|
||||
</h3>
|
||||
<p className="col-span-2 text-sm leading-relaxed text-slate-600">
|
||||
<Trans
|
||||
i18nKey="pricing:whyUpgradeAnswer"
|
||||
defaults="When you upgrade to a paid plan, you will be able to finalize your polls and automatically send calendar invites to your participants with your selected date. We will also keep your polls indefinitely so they won't be automatically deleted even after they are finalized."
|
||||
></Trans>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-2 py-4 md:grid-cols-3">
|
||||
<h3 className="col-span-1">
|
||||
<Trans
|
||||
i18nKey="pricing:howToUpgrade"
|
||||
defaults="How do I upgrade to a paid plan?"
|
||||
/>
|
||||
</h3>
|
||||
<p className="col-span-2 text-sm leading-relaxed text-slate-600">
|
||||
<Trans
|
||||
i18nKey="pricing:howToUpgradeAnswer"
|
||||
components={{
|
||||
a: (
|
||||
<Link
|
||||
className="text-link"
|
||||
href={linkToApp("/settings/billing")}
|
||||
/>
|
||||
),
|
||||
b: <strong />,
|
||||
}}
|
||||
defaults="To upgrade, you can go to your <a>billing settings</a> and click on <b>Upgrade</b>."
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-x-8 gap-y-2 py-4 md:grid-cols-3">
|
||||
<h3 className="col-span-1">
|
||||
<Trans
|
||||
i18nKey="pricing:cancelSubscription"
|
||||
defaults="How do I cancel my subscription?"
|
||||
></Trans>
|
||||
</h3>
|
||||
<p className="col-span-2 text-sm leading-relaxed text-slate-600">
|
||||
<Trans
|
||||
i18nKey="pricing:cancelSubscriptionAnswer"
|
||||
components={{
|
||||
a: (
|
||||
<Link
|
||||
className="text-link"
|
||||
href={linkToApp("/settings/billing")}
|
||||
/>
|
||||
),
|
||||
b: <strong />,
|
||||
}}
|
||||
defaults="You can cancel your subscription at any time by going to your <a>billing settings</a>. Once you cancel your subscription, you will still have access to your paid plan until the end of your billing period. After that, you will be downgraded to a free plan."
|
||||
></Trans>
|
||||
</p>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
<hr />
|
||||
<FAQ />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import { CheckCircle2Icon, TrendingUpIcon } from "@rallly/icons";
|
||||
import { cn } from "@rallly/ui";
|
||||
import { BillingPlanPeriod, BillingPlanPrice } from "@rallly/ui/billing-plan";
|
||||
import { TrendingUpIcon } from "@rallly/icons";
|
||||
import {
|
||||
BillingPlan,
|
||||
BillingPlanDescription,
|
||||
BillingPlanHeader,
|
||||
BillingPlanPeriod,
|
||||
BillingPlanPerk,
|
||||
BillingPlanPerks,
|
||||
BillingPlanPrice,
|
||||
BillingPlanTitle,
|
||||
} from "@rallly/ui/billing-plan";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/tabs";
|
||||
import React from "react";
|
||||
|
@ -9,27 +17,6 @@ import { Trans } from "@/components/trans";
|
|||
import { UpgradeButton } from "@/components/upgrade-button";
|
||||
import { annualPriceUsd, monthlyPriceUsd } from "@/utils/constants";
|
||||
|
||||
const Perks = ({ children }: React.PropsWithChildren) => {
|
||||
return <ul className="grid gap-1">{children}</ul>;
|
||||
};
|
||||
|
||||
const Perk = ({
|
||||
children,
|
||||
pro,
|
||||
}: React.PropsWithChildren<{ pro?: boolean }>) => {
|
||||
return (
|
||||
<li className="flex items-start gap-x-2.5">
|
||||
<CheckCircle2Icon
|
||||
className={cn(
|
||||
"mt-0.5 h-4 w-4 shrink-0",
|
||||
!pro ? "text-gray-500" : "text-primary",
|
||||
)}
|
||||
/>
|
||||
<div className="text-sm">{children}</div>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
export const BillingPlans = () => {
|
||||
const [tab, setTab] = React.useState("yearly");
|
||||
|
||||
|
@ -45,18 +32,18 @@ export const BillingPlans = () => {
|
|||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className="grid gap-4 rounded-md md:grid-cols-2">
|
||||
<div className="space-y-4 rounded-md border p-4">
|
||||
<div>
|
||||
<h3>
|
||||
<BillingPlan>
|
||||
<BillingPlanHeader>
|
||||
<BillingPlanTitle>
|
||||
<Trans i18nKey="planFree" />
|
||||
</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
</BillingPlanTitle>
|
||||
<BillingPlanDescription>
|
||||
<Trans
|
||||
i18nKey="planFreeDescription"
|
||||
defaults="For casual users"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</BillingPlanDescription>
|
||||
</BillingPlanHeader>
|
||||
<div>
|
||||
<BillingPlanPrice>$0</BillingPlanPrice>
|
||||
<BillingPlanPeriod>
|
||||
|
@ -67,21 +54,21 @@ export const BillingPlans = () => {
|
|||
<Button disabled className="w-full">
|
||||
<Trans i18nKey="currentPlan" defaults="Current Plan" />
|
||||
</Button>
|
||||
<Perks>
|
||||
<Perk>
|
||||
<BillingPlanPerks>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="limitedAccess"
|
||||
defaults="Access to core features"
|
||||
/>
|
||||
</Perk>
|
||||
<Perk>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk>
|
||||
<Trans
|
||||
i18nKey="pollsDeleted"
|
||||
defaults="Polls are automatically deleted once they become inactive"
|
||||
/>
|
||||
</Perk>
|
||||
</Perks>
|
||||
</div>
|
||||
</BillingPlanPerk>
|
||||
</BillingPlanPerks>
|
||||
</BillingPlan>
|
||||
<div className="space-y-4 rounded-md border p-4">
|
||||
<div>
|
||||
<h3>
|
||||
|
@ -120,23 +107,23 @@ export const BillingPlans = () => {
|
|||
</div>
|
||||
<hr />
|
||||
<UpgradeButton annual={tab === "yearly"} />
|
||||
<Perks>
|
||||
<Perk pro={true}>
|
||||
<BillingPlanPerks>
|
||||
<BillingPlanPerk pro={true}>
|
||||
<Trans
|
||||
i18nKey="accessAllFeatures"
|
||||
defaults="Access all features"
|
||||
/>
|
||||
</Perk>
|
||||
<Perk pro={true}>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk pro={true}>
|
||||
<Trans i18nKey="plan_extendedPollLife" />
|
||||
</Perk>
|
||||
<Perk pro={true}>
|
||||
</BillingPlanPerk>
|
||||
<BillingPlanPerk pro={true}>
|
||||
<Trans
|
||||
i18nKey="earlyAccess"
|
||||
defaults="Get early access to new features"
|
||||
/>
|
||||
</Perk>
|
||||
</Perks>
|
||||
</BillingPlanPerk>
|
||||
</BillingPlanPerks>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs>
|
||||
|
|
|
@ -1,46 +1,38 @@
|
|||
import { CheckIcon } from "@rallly/icons";
|
||||
import { cva, VariantProps } from "class-variance-authority";
|
||||
import { CheckCircle2Icon } from "@rallly/icons";
|
||||
|
||||
import { cn } from "./lib/utils";
|
||||
|
||||
const billingPlanVariants = cva(
|
||||
"border flex flex-col rounded-md shadow-sm overflow-hidden divide-y",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
primary: "bg-white",
|
||||
default: "bg-gray-50",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export const BillingPlan = ({
|
||||
variant = "default",
|
||||
children,
|
||||
}: React.PropsWithChildren<VariantProps<typeof billingPlanVariants>>) => {
|
||||
return <div className={billingPlanVariants({ variant })}>{children}</div>;
|
||||
className,
|
||||
}: React.PropsWithChildren<{ className?: string }>) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"space-y-4 rounded-md border bg-gradient-to-b from-white to-white/75 px-5 py-4 backdrop-blur-sm",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const BillingPlanHeader = ({
|
||||
children,
|
||||
className,
|
||||
}: React.PropsWithChildren<{ className?: string }>) => {
|
||||
return <div className={cn("px-4 py-3", className)}>{children}</div>;
|
||||
return <div className={cn(className)}>{children}</div>;
|
||||
};
|
||||
|
||||
export const BillingPlanTitle = ({
|
||||
export const BillingPlanTitle = ({ children }: React.PropsWithChildren) => {
|
||||
return <h3>{children}</h3>;
|
||||
};
|
||||
|
||||
export const BillingPlanDescription = ({
|
||||
children,
|
||||
className,
|
||||
}: React.PropsWithChildren<{ className?: string }>) => {
|
||||
return (
|
||||
<h1 className={cn("mb-2 text-xl font-bold tracking-tight", className)}>
|
||||
{children}
|
||||
</h1>
|
||||
);
|
||||
}: React.PropsWithChildren) => {
|
||||
return <p className="text-muted-foreground text-sm">{children}</p>;
|
||||
};
|
||||
|
||||
export const BillingPlanPrice = ({
|
||||
|
@ -68,18 +60,22 @@ export const BillingPlanPeriod = ({ children }: React.PropsWithChildren) => {
|
|||
};
|
||||
|
||||
export const BillingPlanPerks = ({ children }: React.PropsWithChildren) => {
|
||||
return <ul className="grow space-y-1 p-4 text-sm">{children}</ul>;
|
||||
return <ul className="grid gap-1">{children}</ul>;
|
||||
};
|
||||
|
||||
export const BillingPlanPerk = ({ children }: React.PropsWithChildren) => {
|
||||
export const BillingPlanPerk = ({
|
||||
children,
|
||||
pro,
|
||||
}: React.PropsWithChildren<{ pro?: boolean }>) => {
|
||||
return (
|
||||
<li className="flex items-center gap-x-2 ">
|
||||
<CheckIcon className="h-4 w-4 text-green-600" />
|
||||
<span>{children}</span>
|
||||
<li className="flex items-start gap-x-2.5">
|
||||
<CheckCircle2Icon
|
||||
className={cn(
|
||||
"mt-0.5 h-4 w-4 shrink-0",
|
||||
!pro ? "text-gray-500" : "text-primary",
|
||||
)}
|
||||
/>
|
||||
<div className="text-sm">{children}</div>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
export const BillingPlanFooter = ({ children }: React.PropsWithChildren) => {
|
||||
return <div className="p-4">{children}</div>;
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ const buttonVariants = cva(
|
|||
destructive:
|
||||
"bg-destructive text-destructive-foreground active:bg-destructive hover:bg-destructive/90",
|
||||
default:
|
||||
"rounded-md px-3.5 py-2.5 hover:shadow-sm active:shadow-none data-[state=open]:shadow-none data-[state=open]:bg-gray-100 active:bg-gray-100 hover:bg-white/50 bg-gray-50",
|
||||
"rounded-md px-3.5 py-2.5 data-[state=open]:shadow-none hover:border-gray-100 data-[state=open]:bg-gray-100 active:bg-gray-100 hover:bg-white/50 bg-gray-50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "border-transparent hover:bg-gray-200 active:bg-gray-300",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue