محاسبهٔ وام
پیشنمایش تمامصفحهمبلغ وام، تعداد اقساط و نرخ سود با محاسبهٔ قسط ماهانه و جمع بازپرداخت.
۱. نصب کامپوننتها
این بلاک از ۶ آیتم دیگ استفاده میکند: buttoncontrol-sizelabelpersianslidertoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/slider.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/control-size.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/slider.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/control-size.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/slider.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/control-size.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/slider.json https://docs.digdesign.ir/r/toggle-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
loan-calculator.tsx
"use client"
import * as React from "react"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Label } from "@/components/ui/label"
import { Slider } from "@/components/ui/slider"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatCurrency, formatPercent, toFaDigits } from "@/lib/persian"
const MONTHS = [6, 12, 18, 24, 36]
const ANNUAL_RATE = 0.23
/** قسطِ ماهانه با فرمولِ استانداردِ اقساطِ مساوی. */
function monthlyPayment(principal: number, months: number, annualRate: number) {
const r = annualRate / 12
return (principal * r) / (1 - (1 + r) ** -months)
}
/**
* محاسبهگرِ وام: مبلغ با اسلایدر و مدت با سگمنت؛ کارتِ تیرهٔ نتیجه با قسطِ ماهانهٔ درشت،
* نوارِ نسبتِ اصلِ وام به سود، و ریزِ مبالغ. دکمهٔ درخواست با فاصلهٔ بیشتر در پایان.
*/
export default function LoanCalculatorBlock() {
const [amount, setAmount] = React.useState([150_000_000])
const [months, setMonths] = React.useState("24")
const installment = monthlyPayment(amount[0], Number(months), ANNUAL_RATE)
const total = installment * Number(months)
const interest = total - amount[0]
const principalShare = (amount[0] / total) * 100
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto flex w-full max-w-4xl 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">نرخِ سودِ سالانه {formatPercent(ANNUAL_RATE)}، اقساطِ مساوی</p>
</header>
<div className="grid grid-cols-1 items-stretch gap-6 md:grid-cols-2">
<section className="flex flex-col gap-8 rounded-xl border border-border bg-card p-6">
<div className="flex flex-col gap-4">
<div className="flex items-baseline justify-between gap-2">
<Label id="loan-amount">مبلغِ وام</Label>
<span className="text-xl font-bold tabular-nums">{formatCurrency(amount[0])}</span>
</div>
<Slider aria-labelledby="loan-amount" value={amount} onValueChange={setAmount} min={10_000_000} max={500_000_000} step={10_000_000} />
<div className="flex justify-between text-xs text-muted-foreground">
<span>{formatCurrency(10_000_000)}</span>
<span>{formatCurrency(500_000_000)}</span>
</div>
</div>
<div className="flex flex-col gap-3">
<span id="loan-months" className="text-sm font-medium">
مدتِ بازپرداخت (ماه)
</span>
<ToggleGroup type="single" variant="segmented" value={months} onValueChange={(value) => value && setMonths(value as string)} aria-labelledby="loan-months" className="w-full">
{MONTHS.map((value) => (
<ToggleGroupItem key={value} value={String(value)} className="flex-1">
{toFaDigits(value)}
</ToggleGroupItem>
))}
</ToggleGroup>
</div>
</section>
<section aria-live="polite" className="flex flex-col gap-6 rounded-xl bg-inverse p-6 text-[var(--dig-white)] [--muted-foreground:var(--dig-neutral-400)]">
<div className="flex flex-col gap-1">
<span className="text-sm text-muted-foreground">قسطِ ماهانه</span>
<span className="text-4xl font-black tabular-nums">{formatCurrency(Math.round(installment))}</span>
</div>
<div className="flex flex-col gap-3">
<div className="flex h-3 overflow-hidden rounded-full" role="img" aria-label={`اصلِ وام ${toFaDigits(Math.round(principalShare))} درصدِ کلِ بازپرداخت`}>
<span className="bg-[var(--dig-blue-400)]" style={{ width: `${principalShare}%` }} />
<span className="flex-1 bg-[var(--dig-orange-400)]" />
</div>
<dl className="flex flex-col gap-3 text-sm">
<div className="flex justify-between gap-2">
<dt className="flex items-center gap-2 text-muted-foreground">
<span aria-hidden className="size-2.5 rounded-full bg-[var(--dig-blue-400)]" />
اصلِ وام
</dt>
<dd className="tabular-nums">{formatCurrency(amount[0])}</dd>
</div>
<div className="flex justify-between gap-2">
<dt className="flex items-center gap-2 text-muted-foreground">
<span aria-hidden className="size-2.5 rounded-full bg-[var(--dig-orange-400)]" />
سودِ کل
</dt>
<dd className="tabular-nums">{formatCurrency(Math.round(interest))}</dd>
</div>
<div className="flex justify-between gap-2 border-t border-inverse-border pt-3 font-semibold">
<dt>مجموعِ بازپرداخت</dt>
<dd className="tabular-nums">{formatCurrency(Math.round(total))}</dd>
</div>
</dl>
</div>
<Button fullWidth className="mt-auto bg-[var(--dig-white)] text-[var(--dig-graphite-900)] hover:bg-[var(--dig-neutral-200)]">
درخواستِ وام
</Button>
</section>
</div>
</main>
</ControlSizeProvider>
)
}