بلاک‌ها · بانکی و پرداخت

مبلغ وام، تعداد اقساط و نرخ سود با محاسبهٔ قسط ماهانه و جمع بازپرداخت.

۱. نصب کامپوننت‌ها

این بلاک از ۶ آیتم دیگ استفاده می‌کند: 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.json
pnpm 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.json
yarn 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.json
bunx --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>
  )
}