داشبوردِ کیفپول
پیشنمایش تمامصفحهنمای کلیِ کیفپول: موجودیِ کل با دو اقدام، کارتِ پساندازِ سبزِ روشن با هدفها، حلقهٔ هزینهها، سقفهای کارت با هشدارِ نزدیک به سقف و آخرین تراکنشها.
۱. نصب کامپوننتها
این بلاک از ۹ آیتم دیگ استفاده میکند: avatarbuttoncardchart-presetscontrol-sizeiconspersianprogresstoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/chart-presets.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/toggle-group.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/chart-presets.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/toggle-group.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/chart-presets.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/toggle-group.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/chart-presets.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/toggle-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
dashboard-wallet.tsx
"use client"
import * as React from "react"
import { ArrowUpIcon, BellIcon, MoreHorizontalIcon, PlusIcon, SearchIcon, ShopIcon, ShoppingCartIcon, SparklesIcon, TruckIcon, WalletIcon } from "@/components/icons"
import { Avatar } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { RadialChart } from "@/components/ui/chart-presets"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Progress } from "@/components/ui/progress"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatNumber, toFaDigits } from "@/lib/persian"
import { stockImages } from "@/lib/stock-images"
const SPENDING = [
{ name: "خرید", value: 38 },
{ name: "رستوران", value: 24 },
{ name: "حملونقل", value: 18 },
]
const LIMITS = [
{ label: "خریدِ کارتی", used: 42_000_000, max: 80_000_000 },
{ label: "برداشتِ نقدی", used: 18_500_000, max: 20_000_000 },
{ label: "انتقالِ کارتبهکارت", used: 6_000_000, max: 50_000_000 },
]
const TRANSACTIONS = [
{ icon: <ShopIcon />, title: "فروشگاهِ افق", time: "امروز، ۱۲:۴۰", amount: -1_280_000, tone: "bg-[var(--dig-orange-100)] text-[var(--dig-orange-700)] dark:bg-[var(--dig-orange-950)] dark:text-[var(--dig-orange-300)]" },
{ icon: <WalletIcon />, title: "واریزِ حقوق", time: "دیروز، ۰۹:۰۰", amount: 96_000_000, tone: "bg-[var(--dig-green-100)] text-[var(--dig-green-700)] dark:bg-[var(--dig-green-950)] dark:text-[var(--dig-green-300)]" },
{ icon: <TruckIcon />, title: "اسنپباکس", time: "دیروز، ۱۸:۲۰", amount: -320_000, tone: "bg-[var(--dig-blue-100)] text-[var(--dig-blue-700)] dark:bg-[var(--dig-blue-950)] dark:text-[var(--dig-blue-300)]" },
{ icon: <ShoppingCartIcon />, title: "سوپرمارکتِ محله", time: "۱۴ مهر", amount: -2_150_000, tone: "bg-[var(--dig-violet-100)] text-[var(--dig-violet-700)] dark:bg-[var(--dig-violet-950)] dark:text-[var(--dig-violet-300)]" },
]
/**
* نمای کلیِ کیفپول: سلام و نوارِ بخشها، کارتِ موجودی با عددِ درشت و دو اقدام، کارتِ
* پساندازِ سبزِ روشن با نشانِ ستاره و دو هدف، حلقهٔ هزینهها بر اساسِ دسته، سقفهای کارت با
* نوارِ مصرف (نزدیک به سقف هشدار میگیرد) و آخرین تراکنشها.
*/
export default function DashboardWalletBlock() {
const [section, setSection] = React.useState("overview")
return (
<ControlSizeProvider size="xl" asChild>
<main className="flex w-full flex-col gap-6 bg-subtle p-4 sm:p-6">
<header className="mx-auto flex w-full max-w-6xl flex-wrap items-center justify-between gap-4">
<div className="flex items-center gap-3">
<Avatar src={stockImages.personMan1.src} alt="" />
<span className="text-lg font-bold">سلام، امیر</span>
</div>
<ToggleGroup type="single" variant="segmented" value={section} onValueChange={(value) => value && setSection(value as string)} aria-label="بخشها" className="order-3 w-full overflow-x-auto md:order-none md:w-auto">
<ToggleGroupItem value="overview">نمای کلی</ToggleGroupItem>
<ToggleGroupItem value="cards">کارتها</ToggleGroupItem>
<ToggleGroupItem value="invest">سرمایه</ToggleGroupItem>
<ToggleGroupItem value="history">تراکنشها</ToggleGroupItem>
</ToggleGroup>
<div className="flex gap-2">
<Button variant="ghost" size="icon" aria-label="جستوجو">
<SearchIcon />
</Button>
<Button variant="ghost" size="icon" aria-label="اعلانها">
<BellIcon />
</Button>
</div>
</header>
<div className="mx-auto grid w-full max-w-6xl grid-cols-1 gap-4 lg:grid-cols-2">
<Card>
<CardHeader>
<CardTitle className="text-sm font-medium text-muted-foreground">موجودیِ کل</CardTitle>
<CardAction className="flex gap-1 text-sm">
<span className="rounded-full bg-muted px-2.5 py-1 font-semibold">تومان</span>
<span className="rounded-full px-2.5 py-1 text-muted-foreground">دلار</span>
</CardAction>
</CardHeader>
<CardContent className="flex flex-col gap-8">
<span className="flex items-center gap-3">
<span className="text-4xl font-black tabular-nums sm:text-5xl">{formatNumber(342_180_000)}</span>
<bdi dir="ltr" className="rounded-full bg-success-subtle px-2 py-0.5 text-xs font-semibold text-success">
+٪۴٫۲
</bdi>
</span>
<div className="flex items-center justify-between gap-3">
<div className="flex gap-2">
<Button>
<ArrowUpIcon className="-rotate-45" />
انتقال
</Button>
<Button variant="outline">
<PlusIcon />
افزایشِ موجودی
</Button>
</div>
<Button variant="ghost" size="icon" aria-label="اقدامهای بیشتر" className="hidden sm:inline-flex">
<MoreHorizontalIcon />
</Button>
</div>
</CardContent>
</Card>
<section aria-labelledby="dw-save" className="relative isolate flex flex-col justify-between gap-8 overflow-hidden rounded-xl bg-[var(--dig-green-300)] p-6 text-[var(--dig-graphite-950)]">
<SparklesIcon aria-hidden className="absolute -end-6 -top-6 -z-10 size-48 text-[var(--dig-green-200)]" />
<div className="flex items-start justify-between">
<div className="flex flex-col gap-2">
<span id="dw-save" className="w-fit rounded-full bg-[var(--dig-graphite-950)] px-3 py-1 text-xs font-semibold text-[var(--dig-green-200)]">
پسانداز
</span>
<span className="text-4xl font-black tabular-nums">{formatNumber(121_720_000)}</span>
<span className="text-sm text-[var(--dig-green-900)]">سودِ روزشمار ٪{toFaDigits(23)} سالانه</span>
</div>
</div>
<ul className="grid grid-cols-2 gap-3">
{[
["سفرِ نوروز", 64],
["صندوقِ اضطرار", 88],
].map(([label, value]) => (
<li key={label} className="flex flex-col gap-2 rounded-lg bg-[var(--dig-green-200)] p-3">
<span className="flex justify-between text-sm font-semibold">
{label}
<span className="tabular-nums">٪{toFaDigits(value)}</span>
</span>
<span aria-hidden className="h-1.5 overflow-hidden rounded-full bg-[var(--dig-green-100)]">
<span className="block h-full rounded-full bg-[var(--dig-graphite-950)]" style={{ width: `${value}%` }} />
</span>
</li>
))}
</ul>
</section>
</div>
<div className="mx-auto grid w-full max-w-6xl grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<Card>
<CardHeader>
<CardTitle>هزینهها</CardTitle>
<CardAction className="text-sm text-muted-foreground">این ماه</CardAction>
</CardHeader>
<CardContent>
<RadialChart
className="h-56"
data={SPENDING}
nameKey="name"
valueKey="value"
max={100}
showTrack
showLabels
centerLabel={{ value: "۱۸٫۴ م", label: "کلِ هزینه" }}
valueFormatter={(value) => `٪${toFaDigits(value)}`}
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>سقفِ کارت</CardTitle>
<CardAction className="text-sm text-muted-foreground">روزانه</CardAction>
</CardHeader>
<CardContent className="flex flex-col gap-5">
{LIMITS.map((limit) => {
const ratio = Math.round((limit.used / limit.max) * 100)
return (
<div key={limit.label} className="flex flex-col gap-2">
<span className="flex items-baseline justify-between gap-2 text-sm">
<span className="font-semibold">{limit.label}</span>
<span className="text-muted-foreground tabular-nums">
{formatNumber(limit.used / 1_000_000)} از {formatNumber(limit.max / 1_000_000)} میلیون
</span>
</span>
<Progress value={ratio} color={ratio > 85 ? "warning" : "default"} aria-label={`مصرفِ ${limit.label}`} />
</div>
)
})}
</CardContent>
</Card>
<Card className="md:col-span-2 lg:col-span-1">
<CardHeader>
<CardTitle>تراکنشها</CardTitle>
<CardAction>
<Button variant="link" className="h-auto p-0">
همه
</Button>
</CardAction>
</CardHeader>
<CardContent>
<ul className="flex flex-col gap-4">
{TRANSACTIONS.map((item) => (
<li key={item.title} className="flex items-center gap-3">
<span aria-hidden className={`flex size-10 shrink-0 items-center justify-center rounded-full [&>svg]:size-5 ${item.tone}`}>
{item.icon}
</span>
<span className="flex flex-1 flex-col">
<span className="text-sm font-semibold">{item.title}</span>
<span className="text-xs text-muted-foreground">{item.time}</span>
</span>
<bdi dir="ltr" className={`text-sm font-bold tabular-nums ${item.amount > 0 ? "text-success" : ""}`}>
{item.amount > 0 ? "+" : "−"}
{formatNumber(Math.abs(item.amount))}
</bdi>
</li>
))}
</ul>
</CardContent>
</Card>
</div>
</main>
</ControlSizeProvider>
)
}