مقایسهٔ پلنها
پیشنمایش تمامصفحهقیمتگذاری با جدولِ مقایسهٔ کامل: نام، قیمت و دکمهٔ هر پلن در سرِ ستون، قابلیتها در گروههای جدا با تیک و خط، ستونِ پیشنهادی با زمینهٔ ملایم، دورهٔ ماهانه یا سالانه با تب.
۱. نصب کامپوننتها
این بلاک از ۷ آیتم دیگ استفاده میکند: badgebuttoncontrol-sizeiconspersiantabletoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toggle-group.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toggle-group.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toggle-group.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toggle-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
pricing-compare.tsx
"use client"
import * as React from "react"
import { CheckIcon, MinusIcon } from "@/components/icons"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatNumber } from "@/lib/persian"
const PLANS = [
{ id: "basic", title: "پایه", monthly: 0, note: "برای شروع" },
{ id: "pro", title: "حرفهای", monthly: 490_000, note: "برای تیمهای کوچک", featured: true },
{ id: "team", title: "سازمانی", monthly: 1_900_000, note: "برای سازمانها" },
] as const
type Value = boolean | string
/** هر ردیف: یک قابلیت و مقدارش برای هر پلن (true، false یا متن). */
const GROUPS: { title: string; rows: { label: string; values: [Value, Value, Value] }[] }[] = [
{
title: "پایه",
rows: [
{ label: "پروژه", values: ["۱", "۱۰", "نامحدود"] },
{ label: "عضوِ تیم", values: ["۱", "۵", "نامحدود"] },
{ label: "فضای ذخیرهسازی", values: ["۵۰۰ مگابایت", "۲۰ گیگابایت", "نامحدود"] },
],
},
{
title: "گزارش و تحلیل",
rows: [
{ label: "گزارشِ ماهانه", values: [true, true, true] },
{ label: "داشبوردِ سفارشی", values: [false, true, true] },
{ label: "خروجی اکسل و API", values: [false, true, true] },
{ label: "گزارشِ پیشرفته", values: [false, false, true] },
],
},
{
title: "امنیت و پشتیبانی",
rows: [
{ label: "ورودِ دومرحلهای", values: [true, true, true] },
{ label: "ورودِ یکپارچه (SSO)", values: [false, false, true] },
{ label: "پشتیبانی", values: ["ایمیل", "ویژه", "اختصاصی، ۲۴ ساعته"] },
],
},
]
function Mark({ value }: { value: Value }) {
if (typeof value === "string") return <span className="text-sm font-medium">{value}</span>
return value ? (
<CheckIcon aria-label="دارد" className="mx-auto size-5 text-success" />
) : (
<MinusIcon aria-label="ندارد" className="mx-auto size-5 text-muted-foreground" />
)
}
/**
* قیمتگذاری با جدولِ مقایسه: نام و قیمت و دکمهٔ هر پلن در سرِ ستون، قابلیتها در
* گروههای جدا، و ستونِ پیشنهادی با زمینهٔ ملایم و برچسب. در موبایل جدول افقی اسکرول
* میخورد و ستونِ قابلیت ثابت میماند.
*/
export default function PricingCompareBlock() {
const [period, setPeriod] = React.useState("monthly")
const yearly = period === "yearly"
const featuredCell = "bg-[color-mix(in_oklab,var(--brand-secondary)_6%,var(--background))]"
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto flex w-full max-w-6xl flex-col gap-10 px-4 py-16 sm:px-6">
<header className="flex flex-col items-center gap-6 text-center">
<div className="flex flex-col items-center gap-3">
<Badge variant="soft">تعرفهها</Badge>
<h1 className="text-4xl leading-snug font-black sm:text-5xl">مقایسهٔ کاملِ پلنها</h1>
<p className="max-w-xl text-lg leading-8 text-muted-foreground">همهٔ قابلیتها کنارِ هم؛ ۱۴ روز رایگان امتحان کنید.</p>
</div>
<ToggleGroup type="single" variant="segmented" value={period} onValueChange={(value) => value && setPeriod(value as string)} aria-label="دورهٔ پرداخت">
<ToggleGroupItem value="monthly">ماهانه</ToggleGroupItem>
<ToggleGroupItem value="yearly">سالانه · ۲ ماه رایگان</ToggleGroupItem>
</ToggleGroup>
</header>
<div>
<Table layout="fixed" className="min-w-[44rem]">
<TableHeader>
<TableRow className="hover:bg-card">
<TableHead className="w-[28%] align-bottom">
<span className="sr-only">قابلیت</span>
</TableHead>
{PLANS.map((plan) => (
<TableHead key={plan.id} className={`h-auto py-6 text-center align-top ${"featured" in plan ? featuredCell : ""}`}>
<div className="flex flex-col items-center gap-2">
<span className="flex items-center gap-2 text-base font-bold text-foreground">
{plan.title}
{"featured" in plan && <Badge color="violet">پیشنهاد ما</Badge>}
</span>
<span className="text-xs font-normal text-muted-foreground">{plan.note}</span>
<span className="mt-2 text-2xl font-black text-foreground tabular-nums">
{plan.monthly === 0 ? "رایگان" : formatNumber(yearly ? plan.monthly * 10 : plan.monthly)}
</span>
<span className="text-xs font-normal text-muted-foreground">
{plan.monthly === 0 ? "برای همیشه" : yearly ? "تومان / سال" : "تومان / ماه"}
</span>
<Button fullWidth variant={"featured" in plan ? "default" : "outline"} className="mt-4">
{plan.monthly === 0 ? "شروع رایگان" : `انتخاب ${plan.title}`}
</Button>
</div>
</TableHead>
))}
</TableRow>
</TableHeader>
{GROUPS.map((group) => (
<TableBody key={group.title}>
<TableRow className="hover:bg-card">
<TableCell colSpan={4} className="bg-muted text-sm font-bold">
{group.title}
</TableCell>
</TableRow>
{group.rows.map((row) => (
<TableRow key={row.label}>
<TableCell className="text-sm text-muted-foreground">{row.label}</TableCell>
{row.values.map((value, index) => (
<TableCell key={PLANS[index].id} className={`text-center ${"featured" in PLANS[index] ? featuredCell : ""}`}>
<Mark value={value} />
</TableCell>
))}
</TableRow>
))}
</TableBody>
))}
</Table>
</div>
<p className="text-center text-sm text-muted-foreground">
پلنِ سازمانی سفارشی لازم دارید�?{" "}
<Button variant="link" className="h-auto p-0">
با فروش صحبت کنید
</Button>
</p>
</main>
</ControlSizeProvider>
)
}