From a76c942edcd359476435610907b8a4016baa4d2c Mon Sep 17 00:00:00 2001 From: mickasmt Date: Mon, 24 Jun 2024 01:21:11 +0200 Subject: [PATCH] feat: add table compare plans --- app/(dashboard)/dashboard/billing/page.tsx | 2 +- app/(marketing)/pricing/page.tsx | 6 +- components/{ => pricing}/billing-info.tsx | 0 components/pricing/compare-plans.tsx | 86 ++++++++++++ components/{ => pricing}/pricing-cards.tsx | 0 components/{ => pricing}/pricing-faq.tsx | 2 +- components/ui/tooltip.tsx | 6 +- config/subscriptions.ts | 148 +++++++++++++++++---- types/index.d.ts | 8 +- 9 files changed, 224 insertions(+), 34 deletions(-) rename components/{ => pricing}/billing-info.tsx (100%) create mode 100644 components/pricing/compare-plans.tsx rename components/{ => pricing}/pricing-cards.tsx (100%) rename components/{ => pricing}/pricing-faq.tsx (97%) diff --git a/app/(dashboard)/dashboard/billing/page.tsx b/app/(dashboard)/dashboard/billing/page.tsx index 6ca5e821..404f661d 100644 --- a/app/(dashboard)/dashboard/billing/page.tsx +++ b/app/(dashboard)/dashboard/billing/page.tsx @@ -1,6 +1,6 @@ import { redirect } from "next/navigation"; -import { BillingInfo } from "@/components/billing-info"; +import { BillingInfo } from "@/components/pricing/billing-info"; import { DashboardHeader } from "@/components/dashboard/header"; import { DashboardShell } from "@/components/dashboard/shell"; import { Icons } from "@/components/shared/icons"; diff --git a/app/(marketing)/pricing/page.tsx b/app/(marketing)/pricing/page.tsx index e57cf656..5c00223b 100644 --- a/app/(marketing)/pricing/page.tsx +++ b/app/(marketing)/pricing/page.tsx @@ -1,8 +1,9 @@ -import { PricingCards } from "@/components/pricing-cards"; -import { PricingFaq } from "@/components/pricing-faq"; import { getCurrentUser } from "@/lib/session"; import { getUserSubscriptionPlan } from "@/lib/subscription"; import { constructMetadata } from "@/lib/utils"; +import { ComparePlans } from "@/components/pricing/compare-plans"; +import { PricingCards } from "@/components/pricing/pricing-cards"; +import { PricingFaq } from "@/components/pricing/pricing-faq"; export const metadata = constructMetadata({ title: "Pricing – SaaS Starter", @@ -21,6 +22,7 @@ export default async function PricingPage() {

+
); diff --git a/components/billing-info.tsx b/components/pricing/billing-info.tsx similarity index 100% rename from components/billing-info.tsx rename to components/pricing/billing-info.tsx diff --git a/components/pricing/compare-plans.tsx b/components/pricing/compare-plans.tsx new file mode 100644 index 00000000..156ec81c --- /dev/null +++ b/components/pricing/compare-plans.tsx @@ -0,0 +1,86 @@ +import { PlansRow } from "@/types"; +import { CircleCheck, CircleHelp } from "lucide-react"; + +import { comparePlans, plansColumns } from "@/config/subscriptions"; +import { + Tooltip, + TooltipContent, + TooltipPortal, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { HeaderSection } from "@/components/shared/header-section"; +import MaxWidthWrapper from "@/components/shared/max-width-wrapper"; + +export function ComparePlans() { + const renderCell = (value: string | boolean | null) => { + if (value === null) return "—"; + if (typeof value === "boolean") + return value ? : "—"; + return value; + }; + + return ( + + + + +
+ + + + + {plansColumns.map((col) => ( + + ))} + + + + {comparePlans.map((row: PlansRow, index: number) => ( + + + {plansColumns.map((col) => ( + + ))} + + ))} + +
+ {col} +
+
+ {row.feature} + {row.tooltip && ( + + + + + + +

{row.tooltip}

+
+
+
+ )} +
+
+ {renderCell(row[col])} +
+
+
+
+ ); +} diff --git a/components/pricing-cards.tsx b/components/pricing/pricing-cards.tsx similarity index 100% rename from components/pricing-cards.tsx rename to components/pricing/pricing-cards.tsx diff --git a/components/pricing-faq.tsx b/components/pricing/pricing-faq.tsx similarity index 97% rename from components/pricing-faq.tsx rename to components/pricing/pricing-faq.tsx index a0f6a2f3..41114dbc 100644 --- a/components/pricing-faq.tsx +++ b/components/pricing/pricing-faq.tsx @@ -5,7 +5,7 @@ import { AccordionTrigger, } from "@/components/ui/accordion"; -import { HeaderSection } from "./shared/header-section"; +import { HeaderSection } from "../shared/header-section"; const pricingFaqData = [ { diff --git a/components/ui/tooltip.tsx b/components/ui/tooltip.tsx index 83658c70..6a306b0f 100644 --- a/components/ui/tooltip.tsx +++ b/components/ui/tooltip.tsx @@ -11,6 +11,8 @@ const Tooltip = TooltipPrimitive.Root const TooltipTrigger = TooltipPrimitive.Trigger +const TooltipPortal = TooltipPrimitive.Portal + const TooltipContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -19,7 +21,7 @@ const TooltipContent = React.forwardRef< ref={ref} sideOffset={sideOffset} className={cn( - "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", + "z-[99999] overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", className )} {...props} @@ -27,4 +29,4 @@ const TooltipContent = React.forwardRef< )) TooltipContent.displayName = TooltipPrimitive.Content.displayName -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } +export { Tooltip, TooltipTrigger, TooltipContent, TooltipPortal, TooltipProvider } diff --git a/config/subscriptions.ts b/config/subscriptions.ts index 7fd763bf..8f9b8c1d 100644 --- a/config/subscriptions.ts +++ b/config/subscriptions.ts @@ -1,20 +1,20 @@ -import { SubscriptionPlan } from "types" -import { env } from "@/env.mjs" +import { PlansRow, SubscriptionPlan } from "types"; +import { env } from "@/env.mjs"; export const pricingData: SubscriptionPlan[] = [ { - title: 'Starter', - description: 'For Beginners', + title: "Starter", + description: "For Beginners", benefits: [ - 'Up to 100 monthly posts', - 'Basic analytics and reporting', - 'Access to standard templates', + "Up to 100 monthly posts", + "Basic analytics and reporting", + "Access to standard templates", ], limitations: [ - 'No priority access to new features.', - 'Limited customer support', - 'No custom branding', - 'Limited access to business resources.', + "No priority access to new features.", + "Limited customer support", + "No custom branding", + "Limited access to business resources.", ], prices: { monthly: 0, @@ -26,18 +26,18 @@ export const pricingData: SubscriptionPlan[] = [ }, }, { - title: 'Pro', - description: 'Unlock Advanced Features', + title: "Pro", + description: "Unlock Advanced Features", benefits: [ - 'Up to 500 monthly posts', - 'Advanced analytics and reporting', - 'Access to business templates', - 'Priority customer support', - 'Exclusive webinars and training.', + "Up to 500 monthly posts", + "Advanced analytics and reporting", + "Access to business templates", + "Priority customer support", + "Exclusive webinars and training.", ], limitations: [ - 'No custom branding', - 'Limited access to business resources.', + "No custom branding", + "Limited access to business resources.", ], prices: { monthly: 15, @@ -49,14 +49,14 @@ export const pricingData: SubscriptionPlan[] = [ }, }, { - title: 'Business', - description: 'For Power Users', + title: "Business", + description: "For Power Users", benefits: [ - 'Unlimited posts', - 'Real-time analytics and reporting', - 'Access to all templates, including custom branding', - '24/7 business customer support', - 'Personalized onboarding and account management.', + "Unlimited posts", + "Real-time analytics and reporting", + "Access to all templates, including custom branding", + "24/7 business customer support", + "Personalized onboarding and account management.", ], limitations: [], prices: { @@ -69,3 +69,97 @@ export const pricingData: SubscriptionPlan[] = [ }, }, ]; + +export const plansColumns = [ + "starter", + "pro", + "business", + "enterprise", +] as const; + +export const comparePlans: PlansRow[] = [ + { + feature: "Access to Basic Analytics", + starter: true, + pro: true, + business: true, + enterprise: "Custom", + tooltip: "All plans include basic analytics for tracking performance.", + }, + { + feature: "Custom Branding", + starter: null, + pro: "500/mo", + business: "1,500/mo", + enterprise: "Unlimited", + tooltip: "Custom branding is available from the Pro plan onwards.", + }, + { + feature: "Priority Support", + starter: null, + pro: "Email", + business: "Email & Chat", + enterprise: "24/7 Support", + tooltip: "Higher plans provide more extensive support options.", + }, + { + feature: "Advanced Reporting", + starter: null, + pro: null, + business: true, + enterprise: "Custom", + tooltip: + "Advanced reporting is available in Business and Enterprise plans.", + }, + { + feature: "Dedicated Manager", + starter: null, + pro: null, + business: null, + enterprise: true, + tooltip: "Enterprise plan includes a dedicated account manager.", + }, + { + feature: "API Access", + starter: "Limited", + pro: "Standard", + business: "Enhanced", + enterprise: "Full", + tooltip: "API access varies by plan level.", + }, + { + feature: "Monthly Webinars", + starter: false, + pro: true, + business: true, + enterprise: "Custom", + tooltip: "Pro and higher plans include access to monthly webinars.", + }, + { + feature: "Custom Integrations", + starter: false, + pro: false, + business: "Available", + enterprise: "Available", + tooltip: + "Custom integrations are available in Business and Enterprise plans.", + }, + { + feature: "Roles and Permissions", + starter: null, + pro: "Basic", + business: "Advanced", + enterprise: "Advanced", + tooltip: + "User roles and permissions management improves with higher plans.", + }, + { + feature: "Onboarding Assistance", + starter: false, + pro: "Self-service", + business: "Assisted", + enterprise: "Full Service", + tooltip: "Higher plans include more comprehensive onboarding assistance.", + }, + // Add more rows as needed +]; diff --git a/types/index.d.ts b/types/index.d.ts index 56776809..6ae42717 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -87,4 +87,10 @@ export type InfoLdg = { image: string; description: string; list: InfoList[]; -} +}; + +// compare plans +export type ColumnType = string | boolean | null; +export type PlansRow = { feature: string; tooltip?: string } & { + [key in (typeof plansColumns)[number]]: ColumnType; +};