صورتحساب و اشتراک
پیشنمایش تمامصفحهپلنِ فعلی روی کارتِ برند با تمدید، ارتقا و لغو با تأیید؛ مصرفِ هر سهمیه با هشدارِ نزدیک به سقف؛ کارتِ بانکیِ ماسکشده و تاریخچهٔ فاکتورها با وضعیت، پرداختِ دوباره و دانلود.
۱. نصب کامپوننتها
این بلاک از ۱۰ آیتم دیگ استفاده میکند: alert-dialogbadgebuttoncardcontrol-sizeiconspersianprogresstabletoast
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/alert-dialog.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.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/progress.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/alert-dialog.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.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/progress.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/alert-dialog.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.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/progress.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/alert-dialog.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.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/progress.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
billing.tsx
"use client"
import * as React from "react"
import { CheckIcon, CreditCardIcon, DownloadIcon, FlashIcon } from "@/components/icons"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Progress } from "@/components/ui/progress"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { toast } from "@/components/ui/toast"
import { formatCurrency, formatJalali, toFaDigits } from "@/lib/persian"
const USAGE = [
{ label: "اعضای تیم", used: 4, limit: 5, unit: "نفر" },
{ label: "پروژهها", used: 7, limit: 10, unit: "پروژه" },
{ label: "فضای ذخیرهسازی", used: 17.4, limit: 20, unit: "گیگابایت" },
]
const INVOICES = [
{ id: "1405-0718", date: "2026-10-01", amount: 490_000, status: "paid" as const },
{ id: "1405-0612", date: "2026-09-01", amount: 490_000, status: "paid" as const },
{ id: "1405-0507", date: "2026-08-01", amount: 490_000, status: "failed" as const },
{ id: "1405-0405", date: "2026-07-01", amount: 290_000, status: "paid" as const },
]
/**
* صورتحساب و اشتراک: پلنِ فعلی روی کارتِ وارونهٔ برند با تمدید و ارتقا، مصرفِ
* هر سهمیه با هشدارِ نزدیک به سقف، روشِ پرداخت و تاریخچهٔ فاکتورها با وضعیت و
* دانلود. لغوِ اشتراک فقط با تأییدِ دوباره.
*/
export default function BillingBlock() {
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto flex w-full max-w-6xl flex-col gap-8 p-4 sm:p-6">
<header className="flex flex-col gap-1">
<h1 className="text-2xl font-bold">صورتحساب و اشتراک</h1>
<p className="text-muted-foreground">پلن، مصرف و فاکتورهای حساب.</p>
</header>
<div className="grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)]">
{/* پلنِ فعلی: کارتِ برند با رنگهای بازتعریفشده. */}
<section
aria-labelledby="billing-plan"
className="relative flex flex-col gap-6 overflow-hidden rounded-xl bg-[var(--dig-violet-800)] p-6 text-[var(--dig-white)] sm:p-8 [--muted-foreground:var(--dig-violet-200)] [--primary-foreground:var(--dig-violet-800)] [--primary:var(--dig-white)]"
>
<span aria-hidden className="absolute -end-20 -top-20 size-64 rounded-full bg-[var(--dig-violet-700)]" />
<div className="relative flex items-start justify-between gap-4">
<div className="flex flex-col gap-2">
<span className="text-sm text-muted-foreground">پلنِ فعلی</span>
<h2 id="billing-plan" className="flex items-center gap-2 text-3xl font-black">
حرفهای
<Badge className="bg-[var(--dig-amber-400)] text-[var(--dig-graphite-950)]">فعال</Badge>
</h2>
</div>
<span aria-hidden className="flex size-12 items-center justify-center rounded-xl bg-[var(--dig-violet-600)] [&>svg]:size-6">
<FlashIcon />
</span>
</div>
<div className="relative flex items-baseline gap-2">
<span className="text-4xl font-black tabular-nums">{formatCurrency(490_000).replace(" تومان", "")}</span>
<span className="text-muted-foreground">تومان / ماه</span>
</div>
<p className="relative text-sm text-muted-foreground">
تمدیدِ خودکار در {formatJalali(new Date("2026-11-01"), "long")} · ۵ عضو، ۱۰ پروژه، ۲۰ گیگابایت
</p>
<div className="relative mt-2 flex flex-wrap gap-3">
<Button>ارتقا به سازمانی</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline" className="border-[var(--dig-violet-500)] bg-[var(--dig-violet-800)] text-[var(--dig-white)] hover:bg-[var(--dig-violet-700)] hover:text-[var(--dig-white)]">
لغوِ اشتراک
</Button>
</AlertDialogTrigger>
<ControlSizeProvider size="xl" asChild>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>اشتراک لغو شود؟</AlertDialogTitle>
<AlertDialogDescription>
تا {formatJalali(new Date("2026-11-01"), "long")} همهٔ امکانات فعال میماند؛ بعد از آن حساب به پلنِ پایه برمیگردد و پروژههای اضافه فقطخواندنی میشوند.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>منصرف شدم</AlertDialogCancel>
<AlertDialogAction variant="destructive" onClick={() => toast.success("اشتراک تا پایانِ دوره فعال میماند و تمدید نمیشود.")}>
لغوِ اشتراک
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</ControlSizeProvider>
</AlertDialog>
</div>
</section>
{/* مصرف: هر سهمیه با نوار؛ نزدیک به سقف رنگِ هشدار و پیشنهادِ ارتقا. */}
<Card>
<CardHeader>
<CardTitle className="text-base">مصرفِ این دوره</CardTitle>
<CardDescription>تا {formatJalali(new Date("2026-11-01"), "long")}</CardDescription>
</CardHeader>
<CardContent className="flex flex-col gap-5">
{USAGE.map((item) => {
const percent = (item.used / item.limit) * 100
const near = percent >= 85
return (
<div key={item.label} className="flex flex-col gap-2">
<div className="flex items-baseline justify-between gap-2 text-sm">
<span className="font-medium">{item.label}</span>
<span className={near ? "font-semibold text-warning tabular-nums" : "text-muted-foreground tabular-nums"}>
{toFaDigits(item.used)} از {toFaDigits(item.limit)} {item.unit}
</span>
</div>
<Progress value={percent} color={near ? "warning" : "primary"} aria-label={`مصرفِ ${item.label}`} />
</div>
)
})}
<p className="rounded-lg bg-[color-mix(in_oklab,var(--dig-amber-500)_10%,var(--background))] p-3 text-sm leading-6">
فضای ذخیرهسازی نزدیکِ سقف است؛ با پلنِ سازمانی نامحدود میشود.
</p>
</CardContent>
</Card>
</div>
<div className="grid grid-cols-1 items-start gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(0,2fr)]">
<Card>
<CardHeader>
<CardTitle className="text-base">روشِ پرداخت</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-4">
{/* کارتِ بانکی: شمارهٔ ماسکشده و تاریخِ انقضا. */}
<div className="flex aspect-[1.6/1] flex-col justify-between rounded-xl bg-linear-to-br from-[var(--dig-graphite-800)] to-[var(--dig-graphite-950)] p-5 text-[var(--dig-white)]">
<div className="flex items-center justify-between">
<span className="text-sm font-semibold">بانکِ سکه</span>
<CreditCardIcon aria-hidden className="size-6 text-[var(--dig-neutral-400)]" />
</div>
<bdi dir="ltr" className="text-start text-lg tracking-widest tabular-nums">
{toFaDigits("6037 •••• •••• 4521")}
</bdi>
<div className="flex items-center justify-between text-xs text-[var(--dig-neutral-300)]">
<span>سارا محمدی</span>
<span className="tabular-nums">{toFaDigits("08/07")}</span>
</div>
</div>
<p className="flex items-center gap-2 text-sm text-muted-foreground">
<CheckIcon aria-hidden className="size-4 text-success" />
برداشتِ خودکار برای تمدید فعال است.
</p>
<Button variant="outline" fullWidth>
تغییرِ کارت
</Button>
</CardContent>
</Card>
<section aria-labelledby="billing-invoices" className="flex flex-col gap-4">
<h2 id="billing-invoices" className="text-lg font-bold">
فاکتورها
</h2>
<div>
<Table stackOnMobile variant="card">
<TableHeader>
<TableRow>
<TableHead>شمارهٔ فاکتور</TableHead>
<TableHead>تاریخ</TableHead>
<TableHead>مبلغ</TableHead>
<TableHead>وضعیت</TableHead>
<TableHead className="w-12 px-0">
<span className="sr-only">دانلود</span>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{INVOICES.map((invoice) => (
<TableRow key={invoice.id}>
<TableCell className="font-medium tabular-nums">{toFaDigits(invoice.id)}</TableCell>
<TableCell>{formatJalali(new Date(invoice.date), "long")}</TableCell>
<TableCell className="tabular-nums">{formatCurrency(invoice.amount)}</TableCell>
<TableCell>
{invoice.status === "paid" ? (
<Badge variant="soft" color="success">
پرداختشده
</Badge>
) : (
<span className="flex items-center gap-2">
<Badge variant="soft" color="destructive">
ناموفق
</Badge>
<Button variant="link" className="h-auto p-0 text-sm">
پرداختِ دوباره
</Button>
</span>
)}
</TableCell>
<TableCell className="w-12 px-0 text-center">
<Button variant="ghost" size="icon" aria-label={`دانلودِ فاکتورِ ${toFaDigits(invoice.id)}`}>
<DownloadIcon />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</section>
</div>
</main>
</ControlSizeProvider>
)
}