تعرفهها
پیشنمایش تمامصفحهپلنها با پرداخت ماهانه و سالانه، پلن پیشنهادی و سؤالات متداول.
۱. نصب کامپوننتها
این بلاک از ۷ آیتم دیگ استفاده میکند: accordionbuttoncontrol-sizeiconspersianpricing-cardtoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/accordion.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/pricing-card.json https://docs.digdesign.ir/r/toggle-group.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/accordion.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/pricing-card.json https://docs.digdesign.ir/r/toggle-group.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/accordion.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/pricing-card.json https://docs.digdesign.ir/r/toggle-group.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/accordion.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/pricing-card.json https://docs.digdesign.ir/r/toggle-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
pricing.tsx
"use client"
import * as React from "react"
import { FlashIcon, ShieldCheckIcon, SparklesIcon } from "@/components/icons"
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Button } from "@/components/ui/button"
import {
PricingCard,
PricingCardDescription,
PricingCardFeature,
PricingCardFeatures,
PricingCardFeaturesHeader,
PricingCardHeader,
PricingCardPrice,
PricingCardIcon,
PricingCardPanel,
PricingCardTitle,
} from "@/components/ui/pricing-card"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatNumber } from "@/lib/persian"
const PLANS = [
{
id: "basic",
icon: SparklesIcon,
title: "پایه",
description: "برای شروع و آزمودنِ امکانات",
monthly: 0,
includes: "امکاناتِ پلنِ رایگان:",
features: ["۱ پروژه", "۱ عضو", "۵۰۰ مگابایت فضا"],
missing: ["پشتیبانی ویژه", "گزارش پیشرفته"],
highlighted: false,
},
{
id: "pro",
icon: FlashIcon,
title: "حرفهای",
description: "برای تیمهای کوچک که سریع رشد میکنند",
monthly: 490_000,
includes: "همهٔ امکاناتِ پایه، بهعلاوه:",
features: ["۱۰ پروژه", "۵ عضو", "۲۰ گیگابایت فضا", "پشتیبانی ویژه"],
missing: ["گزارش پیشرفته"],
highlighted: true,
},
{
id: "team",
icon: ShieldCheckIcon,
title: "سازمانی",
description: "برای سازمانها با نیازِ امنیت و کنترل",
monthly: 1_900_000,
includes: "همهٔ امکاناتِ حرفهای، بهعلاوه:",
features: ["پروژه و عضوِ نامحدود", "فضای نامحدود", "پشتیبانی ویژه", "گزارش پیشرفته", "ورودِ یکپارچه (SSO)"],
missing: [],
highlighted: false,
},
]
const FAQ = [
{ q: "آیا میتوانم پلن را عوض کنم؟", a: "بله، هر زمان؛ مبلغ به نسبت روزهای باقیمانده حساب میشود." },
{ q: "پرداخت سالانه چقدر تخفیف دارد؟", a: "دو ماه رایگان: مبلغ سالانه برابر ده ماه است." },
{ q: "آیا فاکتور رسمی صادر میشود؟", a: "بله، برای همهٔ پلنهای پولی." },
]
export default function PricingBlock() {
const [period, setPeriod] = React.useState("monthly")
const yearly = period === "yearly"
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto flex w-full max-w-5xl flex-col items-center gap-8 p-4 sm:p-8">
{/* سلسلهمراتب: عنوان و زیرعنوان یک گروهِ نزدیکاند، کنترل با فاصلهٔ بیشتر جدا. */}
<header className="flex flex-col items-center gap-6 text-center">
<div className="flex flex-col items-center gap-2">
<h1 className="text-3xl font-bold leading-snug">پلنی که به کارتان میآید</h1>
<p className="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>
<section className="grid grid-cols-1 w-full gap-4 md:grid-cols-3" aria-label="پلنها">
{PLANS.map((plan) => (
<PricingCard key={plan.id} highlighted={plan.highlighted} badge="محبوب">
{/* بخشِ تصمیم: نام، قیمت و دکمه کنارِ هم؛ امکانات بعد از جداکننده. */}
<div className="flex flex-col gap-5">
<PricingCardIcon>
<plan.icon />
</PricingCardIcon>
<PricingCardHeader>
<PricingCardTitle>{plan.title}</PricingCardTitle>
<PricingCardDescription>{plan.description}</PricingCardDescription>
</PricingCardHeader>
<PricingCardPrice
amount={plan.monthly === 0 ? "رایگان" : formatNumber(yearly ? plan.monthly * 10 : plan.monthly)}
period={plan.monthly === 0 ? "برای همیشه" : yearly ? "تومان / سال" : "تومان / ماه"}
/>
<div className="flex flex-col gap-2">
{/* دکمهٔ توپر در هر سه کارت؛ روی کارتِ بنفش سفید با متنِ بنفش (بیشترین تضاد). */}
<Button
fullWidth
className={plan.highlighted ? "[--primary-foreground:var(--brand-secondary-foreground)] [--primary:var(--brand-secondary)]" : undefined}
>
{plan.monthly === 0 ? "شروع رایگان" : `انتخاب ${plan.title}`}
</Button>
<p className="text-center text-xs text-muted-foreground">
{plan.monthly === 0 ? "بدون نیاز به کارت بانکی" : "۱۴ روز رایگان، لغو در هر زمان"}
</p>
</div>
</div>
<PricingCardPanel>
<PricingCardFeaturesHeader title="امکانات" description={plan.includes} />
<PricingCardFeatures>
{plan.features.map((feature) => (
<PricingCardFeature key={feature}>{feature}</PricingCardFeature>
))}
{plan.missing.map((feature) => (
<PricingCardFeature key={feature} included={false}>
{feature}
</PricingCardFeature>
))}
</PricingCardFeatures>
</PricingCardPanel>
</PricingCard>
))}
</section>
<section className="mt-8 w-full max-w-2xl" aria-labelledby="faq-title">
<h2 id="faq-title" className="mb-2 text-xl font-semibold">
سؤالات متداول
</h2>
<Accordion type="single" collapsible>
{FAQ.map((item) => (
<AccordionItem key={item.q} value={item.q}>
<AccordionTrigger>{item.q}</AccordionTrigger>
<AccordionContent className="text-muted-foreground">{item.a}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</section>
</main>
</ControlSizeProvider>
)
}