diff --git a/apps/web/src/views/home/index.tsx b/apps/web/src/views/home/index.tsx index b813ee56..9eae251e 100644 --- a/apps/web/src/views/home/index.tsx +++ b/apps/web/src/views/home/index.tsx @@ -7,7 +7,6 @@ import { IoLogoGithub } from "react-icons/io"; import Button from "~/components/Button"; import { PageHead } from "~/components/PageHead"; -import Pricing from "../pricing/components/Pricing"; import Cta from "./components/Cta"; import FAQs from "./components/Faqs"; import Features from "./components/Features"; @@ -16,8 +15,6 @@ import Layout from "./components/Layout"; export default function HomeView() { const { resolvedTheme } = useTheme(); - const isDarkMode = resolvedTheme === "dark"; - return ( diff --git a/apps/web/src/views/pricing/components/FeatureComparisonTable.tsx b/apps/web/src/views/pricing/components/FeatureComparisonTable.tsx new file mode 100644 index 00000000..f3d75ee5 --- /dev/null +++ b/apps/web/src/views/pricing/components/FeatureComparisonTable.tsx @@ -0,0 +1,297 @@ +import { t } from "@lingui/core/macro"; +import { HiCheckCircle, HiXCircle } from "react-icons/hi2"; + +interface Feature { + key: string; + label: string; + kan: boolean | string; + trello: boolean | string; +} + +interface Section { + name: string; + features: Feature[]; +} + +type FrequencyValue = "monthly" | "annually"; + +const CellValue = ({ value }: { value: boolean | string }) => { + if (typeof value === "string") { + return ( + {value} + ); + } + return value ? ( + + ) : ( + + ); +}; + +const FeatureComparisonTable = ({ + frequencyValue, +}: { + frequencyValue: FrequencyValue; +}) => { + const sections: Section[] = [ + { + name: t`Core features`, + features: [ + { + key: "ulimited-workspaces", + label: t`Unlimited workspaces`, + kan: true, + trello: false, + }, + { + key: "unlimited-boards", + label: t`Unlimited boards`, + kan: true, + trello: false, + }, + { + key: "unlimited-lists", + label: t`Unlimited lists`, + kan: true, + trello: true, + }, + { + key: "unlimited-cards", + label: t`Unlimited cards`, + kan: true, + trello: true, + }, + { + key: "activity-log", + label: t`Activity log`, + kan: true, + trello: true, + }, + { + key: "templates", + label: t`Custom templates`, + kan: true, + trello: false, + }, + { key: "labels", label: t`Labels & filters`, kan: true, trello: false }, + { key: "checklists", label: t`Checklists`, kan: true, trello: true }, + { + key: "import", + label: t`Import from Trello`, + kan: true, + trello: true, + }, + { + key: "visibility", + label: t`Board visibility`, + kan: true, + trello: true, + }, + { + key: "search", + label: t`Intelligent search`, + kan: true, + trello: true, + }, + { + key: "workspace-url", + label: t`Custom workspace link`, + kan: true, + trello: false, + }, + { + key: "this-is-a-joke", + label: t`Owned by Atlassian`, + kan: false, + trello: true, + }, + ], + }, + { + name: t`Teams`, + features: [ + { + key: "pricing", + label: t`Price per user/month`, + kan: frequencyValue === "monthly" ? `$10.00` : `$8.00`, + trello: frequencyValue === "monthly" ? `$12.00` : `$10.00`, + }, + { + key: "comments", + label: t`Comments & mentions`, + kan: true, + trello: true, + }, + { + key: "assignments", + label: t`Assignees`, + kan: true, + trello: true, + }, + { + key: "sharing", + label: t`Board sharing & invites`, + kan: true, + trello: true, + }, + { + key: "invite-links", + label: t`Invite links`, + kan: true, + trello: true, + }, + ], + }, + { + name: t`Platform`, + features: [ + { + key: "api", + label: t`REST API`, + kan: true, + trello: true, + }, + { + key: "open-source", + label: t`Open source`, + kan: true, + trello: false, + }, + { + key: "self-hostable", + label: t`Self-hostable`, + kan: true, + trello: false, + }, + { + key: "white-label", + label: t`White label`, + kan: true, + trello: false, + }, + { + key: "performance", + label: t`Fast & lightweight`, + kan: true, + trello: false, + }, + ], + }, + ]; + + const products = [ + { + id: "kan", + name: t`Kan`, + featured: true, + }, + { + id: "trello", + name: t`Trello`, + featured: false, + }, + ]; + + return ( +
+
+
+ {products.map((product) => ( + + ))} +
+ +
+ {sections.map((section) => ( +
+

+ {section.name} +

+
+ + ))} +
+
+
+ ); +}; + +export default FeatureComparisonTable; diff --git a/apps/web/src/views/pricing/components/PricingTiers.tsx b/apps/web/src/views/pricing/components/PricingTiers.tsx index 6539222f..d0563c50 100644 --- a/apps/web/src/views/pricing/components/PricingTiers.tsx +++ b/apps/web/src/views/pricing/components/PricingTiers.tsx @@ -1,28 +1,26 @@ import Link from "next/link"; import { Radio, RadioGroup } from "@headlessui/react"; import { t } from "@lingui/core/macro"; -import { useState } from "react"; import { HiBolt, HiCheckCircle } from "react-icons/hi2"; import { twMerge } from "tailwind-merge"; -type Frequency = "monthly" | "annually"; +type FrequencyValue = "monthly" | "annually"; -const Pricing = () => { - const frequencies = [ - { - value: "monthly" as Frequency, - label: t`Monthly`, - priceSuffix: t`per user/month`, - }, - { - value: "annually" as Frequency, - label: t`Yearly`, - priceSuffix: t`per user/month`, - }, - ]; - - const [frequency, setFrequency] = useState(frequencies[1]); +interface Frequency { + value: FrequencyValue; + label: string; + priceSuffix: string; +} +const Pricing = ({ + frequency, + frequencies, + setFrequency, +}: { + frequency: Frequency | undefined; + frequencies: Frequency[]; + setFrequency: (frequency: Frequency) => void; +}) => { const tiers = [ { name: t`Individuals`, @@ -35,10 +33,9 @@ const Pricing = () => { features: [ t`1 user`, t`Unlimited boards`, - t`Unlimited lists`, t`Unlimited cards`, - t`Unlimited comments`, - t`Unlimited activity log`, + t`Custom board templates`, + t`Checklists`, ], showPrice: true, }, @@ -48,7 +45,7 @@ const Pricing = () => { href: "signup", buttonText: t`Get Started`, price: { monthly: "$10.00", annually: "$8.00" }, - description: t`Kanban is better with a team. Perfect for small and growing teams looking to collaborate.`, + description: t`Kan is better with a team. Perfect for small and growing teams looking to collaborate.`, featureHeader: t`Everything in the free plan, plus:`, features: [ t`Workspace members`, diff --git a/apps/web/src/views/pricing/index.tsx b/apps/web/src/views/pricing/index.tsx index ca12789f..e6c7e8b3 100644 --- a/apps/web/src/views/pricing/index.tsx +++ b/apps/web/src/views/pricing/index.tsx @@ -1,10 +1,33 @@ import { t } from "@lingui/core/macro"; +import { useTheme } from "next-themes"; +import { useState } from "react"; +import Button from "~/components/Button"; import { PageHead } from "~/components/PageHead"; +import Cta from "../home/components/Cta"; import Layout from "../home/components/Layout"; +import FeatureComparisonTable from "./components/FeatureComparisonTable"; import PricingTiers from "./components/PricingTiers"; +type FrequencyValue = "monthly" | "annually"; + export default function PricingView() { + const { resolvedTheme } = useTheme(); + const frequencies = [ + { + value: "monthly" as FrequencyValue, + label: t`Monthly`, + priceSuffix: t`per user/month`, + }, + { + value: "annually" as FrequencyValue, + label: t`Yearly`, + priceSuffix: t`per user/month`, + }, + ]; + + const [frequency, setFrequency] = useState(frequencies[1]); + return ( @@ -23,7 +46,38 @@ export default function PricingView() { {t`Get started for free, with no usage limits. For collaboration, upgrade to a plan that fits the size of your team.`}

- + + + +
+
+

{t`Features`}

+
+ +

+ {t`Feature breakdown`} +

+

+ {t`Compare our features to see why Kan is the best choice.`} +

+
+ +
+ +
+ +
+
+
+