طلای دیجیتال
پیشنمایش تمامصفحهپنلِ حسابِ مشتری: طلا یا نقره، قیمتِ لحظهای، خرید و فروش با مبلغ یا مقدار، کارمزد و جمع، پیشنهادِ وام با وثیقهٔ طلا، کارتِ دارایی با پنهانکردنِ موجودی و نمودارِ ۲۴ ساعت.
۱. نصب کامپوننتها
این بلاک از ۸ آیتم دیگ استفاده میکند: buttonchart-presetscontrol-sizeiconspersiantext-fieldtoasttoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/button.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/text-field.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/toggle-group.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/button.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/text-field.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/toggle-group.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/button.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/text-field.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/toggle-group.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/button.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/text-field.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/toggle-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
account-gold.tsx
"use client"
import * as React from "react"
import { ChatIcon, ChevronLeftIcon, ClockIcon, DashboardIcon, EditIcon, EyeIcon, EyeOffIcon, HeartIcon, InfoIcon, LocationIcon, LogoutIcon, ShopIcon, TrendingUpIcon, UserIcon, WalletIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import { AreaChart } from "@/components/ui/chart-presets"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { TextField } from "@/components/ui/text-field"
import { toast } from "@/components/ui/toast"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatCurrency, formatNumber, toFaDigits } from "@/lib/persian"
const NAV = [
{ id: "overview", label: "خلاصهٔ فعالیتها", icon: <DashboardIcon /> },
{ id: "orders", label: "سفارشها", icon: <ShopIcon /> },
{ id: "gold", label: "طلای دیجیتال", icon: <TrendingUpIcon /> },
{ id: "lists", label: "لیستهای من", icon: <HeartIcon /> },
{ id: "reviews", label: "دیدگاهها و پرسشها", icon: <ChatIcon /> },
{ id: "addresses", label: "نشانیها", icon: <LocationIcon /> },
{ id: "account", label: "اطلاعاتِ حساب", icon: <UserIcon /> },
]
/** ستونِ کنارِ پنلِ حساب: نام و موبایل، کیف پول با افزایشِ موجودی، و منوی بخشها. */
function AccountNav({ active }: { active: string }) {
return (
<aside className="flex flex-col gap-2 rounded-xl border border-border bg-card p-4 lg:sticky lg:top-6">
<div className="flex items-start justify-between gap-2 px-2 pt-1 pb-4">
<span className="flex flex-col gap-0.5">
<span className="font-bold">آرمیتا رستگار</span>
<bdi dir="ltr" className="text-sm text-muted-foreground tabular-nums">
{toFaDigits("0912 345 6789")}
</bdi>
</span>
<Button variant="ghost" size="icon" aria-label="ویرایشِ اطلاعات" className="-me-2 -mt-2">
<EditIcon />
</Button>
</div>
<div className="flex items-center justify-between gap-2 rounded-lg bg-subtle p-3">
<span className="flex items-center gap-2 text-sm [&>svg]:size-4 [&>svg]:text-muted-foreground">
<WalletIcon aria-hidden />
کیف پول
</span>
<span className="font-bold tabular-nums">{formatCurrency(150_129)}</span>
</div>
<a href="#" className="flex items-center gap-1 self-end px-2 pb-2 text-sm font-medium text-brand-secondary hover:underline">
افزایشِ موجودی
<ChevronLeftIcon aria-hidden className="size-4" />
</a>
<nav aria-label="حسابِ کاربری" className="flex flex-col border-t border-border pt-2">
{NAV.map((item) => (
<a
key={item.id}
href="#"
aria-current={item.id === active ? "page" : undefined}
className={`flex min-h-12 items-center gap-3 rounded-lg px-3 text-sm [&>svg]:size-5 ${item.id === active ? "bg-primary-subtle font-bold" : "text-muted-foreground hover:bg-subtle hover:text-foreground"}`}
>
{item.icon}
{item.label}
</a>
))}
<a href="#" className="flex min-h-12 items-center gap-3 rounded-lg px-3 text-sm text-destructive hover:bg-destructive-subtle [&>svg]:size-5">
<LogoutIcon aria-hidden />
خروج از حساب
</a>
</nav>
</aside>
)
}
const PRICE = { gold: 262_430, silver: 5_108 } // ریال برای هر میلیگرم
const FEE = 0.005
const CHART = [
{ label: "۱۲:۰۰", value: 255_088 },
{ label: "۱۵:۰۰", value: 256_120 },
{ label: "۱۸:۰۰", value: 257_064 },
{ label: "۲۱:۰۰", value: 258_900 },
{ label: "۰۰:۰۰", value: 259_041 },
{ label: "۰۳:۰۰", value: 259_800 },
{ label: "۰۶:۰۰", value: 261_018 },
{ label: "۰۹:۰۰", value: 262_430 },
]
/**
* خرید و فروشِ طلای دیجیتال در پنلِ حساب: انتخابِ طلا/نقره، قیمتِ لحظهایِ هر میلیگرم با نقطهٔ زنده،
* خرید/فروش با سگمنت، مبلغ به ریال یا مقدار به میلیگرم (هر کدام را بنویسی دیگری حساب میشود)، کارمزد
* و جمع، پیشنهادِ «وام با وثیقهٔ طلا» بهجای فروش، و ستونِ دارایی با پنهانکردنِ موجودی و نمودارِ ۲۴ ساعت.
*/
export default function AccountGoldBlock() {
const [metal, setMetal] = React.useState<"gold" | "silver">("gold")
const [side, setSide] = React.useState("buy")
const [rial, setRial] = React.useState("")
const [mg, setMg] = React.useState("")
const [hidden, setHidden] = React.useState(false)
const [loanDismissed, setLoanDismissed] = React.useState(false)
const price = PRICE[metal]
const amount = Number(rial) || 0
const fee = Math.round(amount * FEE)
const name = metal === "gold" ? "طلا" : "نقره"
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto grid w-full max-w-6xl grid-cols-1 items-start gap-6 p-4 sm:p-6 lg:grid-cols-[17rem_minmax(0,1fr)]">
<div className="hidden lg:block">
<AccountNav active="gold" />
</div>
<div className="grid min-w-0 grid-cols-1 items-start gap-6 xl:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
<section aria-labelledby="trade-title" className="flex flex-col gap-5 rounded-xl border border-border bg-card p-5">
<h1 id="trade-title" className="text-xl font-black">
خرید و فروشِ طلای دیجیتال
</h1>
{/* انتخابِ فلز: دو کارتِ بزرگ با رنگِ هر فلز. */}
<div role="radiogroup" aria-label="فلز" className="grid grid-cols-2 gap-3">
{(["gold", "silver"] as const).map((item) => (
<Button
key={item}
role="radio"
aria-checked={metal === item}
variant="outline"
onClick={() => setMetal(item)}
className={`h-auto gap-3 rounded-xl py-4 text-base ${
metal === item
? item === "gold"
? "border-[var(--dig-amber-500)] bg-[color-mix(in_oklab,var(--dig-amber-400)_14%,var(--card))] font-bold hover:bg-[color-mix(in_oklab,var(--dig-amber-400)_18%,var(--card))]"
: "border-[var(--dig-slate-400)] bg-subtle font-bold"
: "bg-card"
}`}
>
<span aria-hidden className={`size-6 rotate-12 rounded-md ${item === "gold" ? "bg-[linear-gradient(135deg,var(--dig-amber-300),var(--dig-amber-600))]" : "bg-[linear-gradient(135deg,var(--dig-slate-200),var(--dig-slate-500))]"}`} />
{item === "gold" ? "طلا" : "نقره"}
</Button>
))}
</div>
<div className="flex flex-col gap-4 rounded-xl border border-border p-4">
<div className="flex items-center justify-between gap-3">
<span className="flex items-center gap-2 text-sm">
<span aria-hidden className="relative flex size-2.5">
<span className="absolute inset-0 animate-ping rounded-full bg-destructive motion-reduce:hidden" />
<span className="relative size-2.5 rounded-full bg-destructive" />
</span>
قیمتِ لحظهایِ ۱ میلیگرم {name}
</span>
<span className="font-black tabular-nums">{formatNumber(price)} ریال</span>
</div>
<ToggleGroup type="single" variant="segmented" value={side} onValueChange={(value) => value && setSide(value as string)} aria-label="خرید یا فروش" className="w-full">
<ToggleGroupItem value="buy" className="flex-1">
خرید
</ToggleGroupItem>
<ToggleGroupItem value="sell" className="flex-1">
فروش
</ToggleGroupItem>
</ToggleGroup>
<TextField
label={side === "buy" ? "مبلغِ پرداختی" : "مبلغِ دریافتی"}
inputMode="numeric"
dir="rtl"
groupDigits
endContent="ریال"
value={rial}
onValueChange={(value) => {
setRial(value)
setMg(value ? String(Math.floor(Number(value) / price)) : "")
}}
/>
<TextField
label={`مقدارِ ${name}`}
inputMode="numeric"
dir="rtl"
groupDigits
endContent="میلیگرم"
value={mg}
onValueChange={(value) => {
setMg(value)
setRial(value ? String(Number(value) * price) : "")
}}
description="مبلغ یا مقدار را بنویس؛ دیگری خودش حساب میشود."
/>
</div>
{side === "sell" && !loanDismissed && (
<div className="flex flex-col gap-3 rounded-xl bg-subtle p-4">
<span className="flex items-center gap-2 font-bold [&>svg]:size-5 [&>svg]:text-brand-secondary">
<InfoIcon aria-hidden />
پول نقد بدونِ فروشِ {name}
</span>
<p className="text-sm leading-6 text-muted-foreground">با وثیقهکردنِ {name}ِ دیجیتالت وام بگیر و داراییات را نگه دار؛ بازپرداخت در ۴ قسط.</p>
<div className="flex gap-2">
<ControlSizeProvider size="lg" asChild>
<Button variant="outline" className="bg-card">
درخواستِ وام
</Button>
</ControlSizeProvider>
<ControlSizeProvider size="lg" asChild>
<Button variant="ghost" onClick={() => setLoanDismissed(true)}>
بعداً
</Button>
</ControlSizeProvider>
</div>
</div>
)}
<dl className="flex flex-col gap-2 text-sm">
<div className="flex justify-between">
<dt className="text-muted-foreground">کارمزدِ {side === "buy" ? "خرید" : "فروش"} (٪۰٫۵)</dt>
<dd className="tabular-nums">{formatNumber(fee)} ریال</dd>
</div>
<div className="flex justify-between border-t border-border pt-2 text-base">
<dt className="font-bold">{side === "buy" ? "جمعِ پرداخت" : "واریز به کیف پول"}</dt>
<dd className="font-black tabular-nums">{formatNumber(side === "buy" ? amount + fee : Math.max(0, amount - fee))} ریال</dd>
</div>
</dl>
<Button disabled={!amount} onClick={() => toast.success(side === "buy" ? `${toFaDigits(mg)} میلیگرم ${name} خریده شد.` : `${toFaDigits(mg)} میلیگرم ${name} فروخته شد.`)}>
{amount ? `${side === "buy" ? "خرید" : "فروش"}ِ ${toFaDigits(formatNumber(Number(mg)))} میلیگرم ${name}` : "مبلغ یا مقدار را بنویس"}
</Button>
</section>
<div className="flex flex-col gap-6">
{/* داراییِ من: کارتِ رنگیِ طلایی با پنهانکردنِ موجودی. */}
<section aria-labelledby="assets-title" className="relative isolate flex flex-col gap-4 overflow-hidden rounded-xl bg-[linear-gradient(135deg,var(--dig-amber-500),var(--dig-orange-600))] p-5 text-[var(--dig-white)]">
<span aria-hidden className="pointer-events-none absolute -end-12 -top-12 -z-10 size-44 rounded-full border-[18px] border-[color-mix(in_oklab,var(--dig-white)_16%,transparent)]" />
<div className="flex items-center justify-between">
<h2 id="assets-title" className="font-bold">
داراییِ من
</h2>
<Button
variant="ghost"
size="icon"
aria-pressed={hidden}
aria-label={hidden ? "نمایشِ موجودی" : "پنهانکردنِ موجودی"}
onClick={() => setHidden((value) => !value)}
className="-me-2 text-[var(--dig-white)] hover:bg-[color-mix(in_oklab,var(--dig-white)_18%,var(--dig-orange-600))] hover:text-[var(--dig-white)]"
>
{hidden ? <EyeIcon /> : <EyeOffIcon />}
</Button>
</div>
<dl className="grid grid-cols-2 gap-3">
{[
["طلا", "۲٬۴۶۰ میلیگرم", formatCurrency(Math.round((2460 * PRICE.gold) / 10))],
["نقره", "۱۸٬۰۰۰ میلیگرم", formatCurrency(Math.round((18000 * PRICE.silver) / 10))],
].map(([label, mgText, toman]) => (
<div key={label} className="flex flex-col gap-1 rounded-lg bg-[color-mix(in_oklab,var(--dig-white)_16%,var(--dig-orange-600))] p-3">
<dt className="text-sm">{label}</dt>
<dd className="flex flex-col">
<span className="text-lg font-black tabular-nums">{hidden ? "••••" : mgText}</span>
<span className="text-xs tabular-nums">{hidden ? "••••" : `≈ ${toman}`}</span>
</dd>
</div>
))}
</dl>
</section>
<section aria-labelledby="chart-title" className="flex flex-col gap-4 rounded-xl border border-border bg-card p-5">
<div className="flex items-center justify-between gap-3">
<h2 id="chart-title" className="font-bold">
قیمتِ ۱ میلیگرم {name} در ۲۴ ساعت
</h2>
<span className="flex items-center gap-1 text-sm font-semibold text-success [&>svg]:size-4">
<TrendingUpIcon aria-hidden />
<bdi dir="ltr">+٪۲٫۹</bdi>
</span>
</div>
<AreaChart className="h-48" data={CHART} categoryKey="label" smooth showPoints={false} valueAxisFromData series={[{ key: "value", label: "ریال", color: "var(--dig-amber-500)" }]} valueFormatter={(value) => formatNumber(value)} />
<Button variant="outline">
<ClockIcon />
تاریخچهٔ خرید و فروش
</Button>
</section>
</div>
</div>
</main>
</ControlSizeProvider>
)
}