صورتحسابِ بانکی
پیشنمایش تمامصفحهصورتحساب با چهار کاشیِ موجودیِ اول و آخر و جمعِ واریز و برداشت، جدول با ستونِ مانده بعد از هر ردیف و دریافتِ 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.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/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.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/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.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/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>
)
}