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

صورت‌حسابِ بانکی

پیش‌نمایش تمام‌صفحه

صورت‌حساب با چهار کاشیِ موجودیِ اول و آخر و جمعِ واریز و برداشت، جدول با ستونِ مانده بعد از هر ردیف و دریافتِ PDF و اکسل.

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

این بلاک از ۷ آیتم دیگ استفاده می‌کند: buttoncontrol-sizeiconspersiantabletoasttoggle-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/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.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/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.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/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.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/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/table.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/toggle-group.json

۲. کد صفحه

کد را در app/<route>/page.tsx بگذارید و متن‌ها، داده و هندلرها را با محصول خودتان عوض کنید.

transactions-statement.tsx
"use client"

import * as React from "react"

import { DownloadIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { toast } from "@/components/ui/toast"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatCurrency, formatNumber, toFaDigits } from "@/lib/persian"

const OPENING = 12_400_000
const ROWS = [
  { date: "۱۴۰۵/۰۷/۰۱", title: "واریزِ حقوق", ref: "882104", amount: 32_000_000 },
  { date: "۱۴۰۵/۰۷/۰۳", title: "اجارهٔ خانه", ref: "882231", amount: -14_500_000 },
  { date: "۱۴۰۵/۰۷/۰۵", title: "قبضِ برق", ref: "882390", amount: -412_000 },
  { date: "۱۴۰۵/۰۷/۰۸", title: "انتقال از امیر رضایی", ref: "882517", amount: 2_000_000 },
  { date: "۱۴۰۵/۰۷/۱۲", title: "فروشگاهِ بوم", ref: "882733", amount: -740_000 },
]

/**
 * صورت‌حسابِ بانکی: موجودی اول و آخرِ دوره و جمعِ واریز و برداشت در چهار کاشی، و جدول با
 * ستونِ «مانده» که بعد از هر ردیف حساب می‌شود؛ شمارهٔ پیگیری چپ‌به‌راست، دریافتِ PDF و اکسل.
 */
export default function TransactionsStatementBlock() {
  const [period, setPeriod] = React.useState("month")
  // ماندهٔ بعد از هر ردیف = موجودی اول + جمعِ تراکنش‌ها تا همان ردیف.
  const rows = ROWS.map((row, index) => ({ ...row, balance: OPENING + ROWS.slice(0, index + 1).reduce((sum, item) => sum + item.amount, 0) }))
  const balance = rows[rows.length - 1].balance
  const credit = ROWS.filter((row) => row.amount > 0).reduce((sum, row) => sum + row.amount, 0)
  const debit = ROWS.filter((row) => row.amount < 0).reduce((sum, row) => sum - row.amount, 0)
  const tiles = [
    { label: "موجودی اولِ دوره", value: OPENING },
    { label: "جمعِ واریز", value: credit, tone: "text-success" },
    { label: "جمعِ برداشت", value: debit },
    { label: "موجودی پایانِ دوره", value: balance, strong: true },
  ]

  return (
    <ControlSizeProvider size="xl" asChild>
      <main className="mx-auto flex w-full max-w-5xl flex-col gap-8 p-4 sm:p-6">
        <header className="flex flex-wrap items-end justify-between gap-x-4 gap-y-6">
          <div className="flex flex-col gap-1">
            <h1 className="text-2xl font-bold">صورت‌حساب</h1>
            <p className="text-muted-foreground">
              حسابِ جاری · <bdi dir="ltr">{toFaDigits("0123-4567-89")}</bdi>
            </p>
          </div>
          <div className="flex flex-wrap items-center gap-3">
            <ToggleGroup type="single" variant="segmented" value={period} onValueChange={(value) => value && setPeriod(value as string)} aria-label="دوره">
              <ToggleGroupItem value="month">این ماه</ToggleGroupItem>
              <ToggleGroupItem value="quarter">سه ماه</ToggleGroupItem>
            </ToggleGroup>
            <Button variant="outline" onClick={() => toast.success("فایلِ PDF آماده شد.")}>
              <DownloadIcon />
              PDF
            </Button>
            <Button variant="outline" onClick={() => toast.success("فایلِ اکسل آماده شد.")}>
              <DownloadIcon />
              اکسل
            </Button>
          </div>
        </header>

        <dl className="grid grid-cols-2 gap-3 lg:grid-cols-4">
          {tiles.map((tile) => (
            <div key={tile.label} className={`flex flex-col gap-1 rounded-xl p-4 ${tile.strong ? "bg-inverse text-[var(--dig-white)] [--muted-foreground:var(--dig-neutral-400)]" : "bg-subtle"}`}>
              <dt className="text-sm text-muted-foreground">{tile.label}</dt>
              <dd className={`text-lg font-black tabular-nums ${tile.tone ?? ""}`}>{formatCurrency(tile.value)}</dd>
            </div>
          ))}
        </dl>

        <div className="rounded-xl border border-border bg-card">
          <Table stackOnMobile className="min-w-[40rem]">
            <TableHeader className="bg-subtle">
              <TableRow className="hover:bg-subtle">
                <TableHead>تاریخ</TableHead>
                <TableHead>شرح</TableHead>
                <TableHead>پیگیری</TableHead>
                <TableHead className="text-end">مبلغ</TableHead>
                <TableHead className="text-end">مانده</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {rows.map((row) => (
                <TableRow key={row.ref}>
                  <TableCell className="text-muted-foreground tabular-nums">{row.date}</TableCell>
                  <TableCell className="font-medium">{row.title}</TableCell>
                  <TableCell className="text-muted-foreground">
                    <bdi dir="ltr">{toFaDigits(row.ref)}</bdi>
                  </TableCell>
                  <TableCell className={`text-end tabular-nums ${row.amount > 0 ? "text-success" : ""}`}>
                    <bdi dir="ltr">
                      {row.amount > 0 ? "+" : "−"}
                      {formatNumber(Math.abs(row.amount))}
                    </bdi>
                  </TableCell>
                  <TableCell className="text-end font-medium tabular-nums">{formatNumber(row.balance)}</TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </div>
        <p className="text-xs text-muted-foreground">مبالغ به تومان است. مانده بعد از هر تراکنش حساب شده.</p>
      </main>
    </ControlSizeProvider>
  )
}