Files
reui-blocks/blocks/pricing30.tsx
T

306 lines
9.0 KiB
TypeScript

"use client";
import { Check, ChevronsUpDown } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { cn } from "@/lib/utils";
type PlanType = "free" | "startup" | "enterprise";
interface Plan {
name: string;
type: PlanType;
button: {
text: string;
variant: "outline";
href: string;
};
features: {
[Category in "usage" | "features" | "support"]: {
name: string;
value: string | boolean;
}[];
};
}
const CATEGORY_LABELS: Record<keyof Plan["features"], string> = {
usage: "میزان استفاده",
features: "امکانات",
support: "پشتیبانی",
};
const plans: Plan[] = [
{
name: "رایگان",
type: "free",
button: {
text: "شروع رایگان",
variant: "outline",
href: "/signup",
},
features: {
usage: [
{ name: "اعضا", value: "نامحدود" },
{ name: "تراکنش‌ها", value: "۲۵۰" },
{ name: "تیم‌ها", value: "۲" },
],
features: [
{ name: "گزارش‌دهی", value: true },
{ name: "تحلیل‌ها", value: true },
{ name: "ورود و خروج داده", value: true },
{ name: "یکپارچه‌سازی‌ها", value: true },
{ name: "هوش مصنوعی نوافروش", value: false },
{ name: "نقش‌های مدیریتی", value: false },
{ name: "گزارش ممیزی", value: false },
],
support: [
{ name: "پشتیبانی اولویت‌دار", value: true },
{ name: "مدیر حساب", value: false },
{ name: "SLA پایداری", value: false },
],
},
},
{
name: "پایه",
type: "startup",
button: {
text: "شروع رایگان",
variant: "outline",
href: "/signup",
},
features: {
usage: [
{ name: "اعضا", value: "نامحدود" },
{ name: "تراکنش‌ها", value: "نامحدود" },
{ name: "تیم‌ها", value: "نامحدود" },
],
features: [
{ name: "گزارش‌دهی", value: true },
{ name: "تحلیل‌ها", value: true },
{ name: "ورود و خروج داده", value: true },
{ name: "یکپارچه‌سازی‌ها", value: true },
{ name: "هوش مصنوعی نوافروش", value: true },
{ name: "نقش‌های مدیریتی", value: false },
{ name: "گزارش ممیزی", value: false },
],
support: [
{ name: "پشتیبانی اولویت‌دار", value: true },
{ name: "مدیر حساب", value: false },
{ name: "SLA پایداری", value: false },
],
},
},
{
name: "سازمانی",
type: "enterprise",
button: {
text: "دریافت دمو",
variant: "outline",
href: "/",
},
features: {
usage: [
{ name: "اعضا", value: "نامحدود" },
{ name: "تراکنش‌ها", value: "نامحدود" },
{ name: "تیم‌ها", value: "نامحدود" },
],
features: [
{ name: "گزارش‌دهی", value: true },
{ name: "تحلیل‌ها", value: true },
{ name: "ورود و خروج داده", value: true },
{ name: "یکپارچه‌سازی‌ها", value: true },
{ name: "هوش مصنوعی نوافروش", value: true },
{ name: "نقش‌های مدیریتی", value: false },
{ name: "گزارش ممیزی", value: false },
],
support: [
{ name: "پشتیبانی اولویت‌دار", value: true },
{ name: "مدیر حساب", value: true },
{ name: "SLA پایداری", value: true },
],
},
},
];
export const Pricing30 = () => {
const [selectedPlan, setSelectedPlan] = useState(1);
return (
<section className="py-32">
<div className="container font-medium">
<MobilePricingTable
selectedPlan={selectedPlan}
onPlanChange={setSelectedPlan}
/>
<DesktopPricingTable />
</div>
</section>
);
};
const MobilePricingTable = ({
selectedPlan,
onPlanChange,
}: {
selectedPlan: number;
onPlanChange: (index: number) => void;
}) => {
const [isOpen, setIsOpen] = useState(false);
const plan = plans[selectedPlan];
return (
<div className="md:hidden">
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<div className="flex items-center justify-between border-b py-4">
<CollapsibleTrigger className="flex items-center gap-2">
<h3 className="text-xl font-semibold">{plan.name}</h3>
<ChevronsUpDown
className={`size-5 transition-transform ${isOpen ? "rotate-180" : ""}`}
/>
</CollapsibleTrigger>
<Button variant={plan.button.variant} className="w-fit">
{plan.button.text}
</Button>
</div>
<CollapsibleContent className="flex flex-col space-y-2 p-2">
{plans.map(
(p, index) =>
index !== selectedPlan && (
<Button
size="lg"
variant="secondary"
key={index}
onClick={() => {
onPlanChange(index);
setIsOpen(false);
}}
>
{p.name}
</Button>
),
)}
</CollapsibleContent>
</Collapsible>
<div className="mt-8">
{Object.entries(plan.features).map(
([category, features], sectionIndex) => (
<div key={sectionIndex} className="mb-8 space-y-2">
<h3 className="mb-4 text-lg font-semibold">
{CATEGORY_LABELS[category as keyof Plan["features"]]}
</h3>
{features.map((feature, featureIndex) => (
<div
key={featureIndex}
className="grid grid-cols-2 items-center gap-8"
>
<span className="border-b py-2">{feature.name}</span>
<div className="flex items-center gap-1 border-b py-2">
{typeof feature.value === "boolean" ? (
feature.value ? (
<Check className="size-5" />
) : (
<span className="size-5" />
)
) : (
<div className="flex items-center gap-1">
<Check className="size-5" />
<span>{feature.value}</span>
</div>
)}
</div>
</div>
))}
</div>
),
)}
</div>
</div>
);
};
const DesktopPricingTable = () => {
return (
<div className="hidden md:grid md:grid-cols-4">
<FeaturesColumn />
{plans.map((plan, index) => (
<PricingColumn
key={plan.name}
plan={plan}
isHighlighted={index === 1}
/>
))}
</div>
);
};
const FeaturesColumn = () => (
<div>
<div className="h-[140px]" />
{Object.entries(plans[0].features).map(([category, features], index) => (
<div key={index}>
<h3 className="flex h-20 items-center text-lg font-semibold">
{CATEGORY_LABELS[category as keyof Plan["features"]]}
</h3>
{features.map((feature, featureIndex) => (
<div key={featureIndex} className="py-4">
{feature.name}
</div>
))}
</div>
))}
</div>
);
const PricingColumn = ({
plan,
isHighlighted,
}: {
plan: Plan;
isHighlighted: boolean;
}) => {
const columnClass = cn("px-6", isHighlighted && "bg-muted border rounded-xl");
return (
<div className={columnClass}>
<div className="py-8">
<h3 className="mb-3 text-xl font-semibold">{plan.name}</h3>
<Button variant={plan.button.variant} asChild>
<a href={plan.button.href}>{plan.button.text}</a>
</Button>
</div>
{Object.entries(plan.features).map(([, features], sectionIndex) => (
<div key={sectionIndex}>
<div className="flex h-20 items-center"></div>
{features.map((feature, featureIndex) => (
<div
key={featureIndex}
className="flex items-center gap-1 border-b py-4"
>
{typeof feature.value === "boolean" ? (
feature.value ? (
<Check className="size-5" />
) : (
<span className="size-5" />
)
) : (
<div className="flex items-center gap-1">
<Check className="size-4 shrink-0" />
<span>{feature.value}</span>
</div>
)}
</div>
))}
</div>
))}
</div>
);
};