بلاک‌ها · معرفی محصول

مقایسهٔ پلن‌ها

پیش‌نمایش تمام‌صفحه

قیمت‌گذاری با جدولِ مقایسهٔ کامل: نام، قیمت و دکمهٔ هر پلن در سرِ ستون، قابلیت‌ها در گروه‌های جدا با تیک و خط، ستونِ پیشنهادی با زمینهٔ ملایم، دورهٔ ماهانه یا سالانه با تب.

۱. نصب کامپوننت‌ها

این بلاک از ۷ آیتم دیگ استفاده می‌کند: 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.json
pnpm 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.json
yarn 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.json
bunx --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>
  )
}