پرداخت قبض
پیشنمایش تمامصفحهپرداخت قبض با نوع قبض، شناسهٔ قبض و پرداخت، و خلاصهٔ مبلغ.
۱. نصب کامپوننتها
این بلاک از ۹ آیتم دیگ استفاده میکند: buttoncardcontrol-sizelabelpersianseparatortext-fieldtoasttoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.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/separator.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/card.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/separator.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/card.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/separator.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/card.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/separator.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 بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
bill-payment.tsx
"use client"
import * as React from "react"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Label } from "@/components/ui/label"
import { Separator } from "@/components/ui/separator"
import { TextField } from "@/components/ui/text-field"
import { toast } from "@/components/ui/toast"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatCurrency } from "@/lib/persian"
const BILL_TYPES = [
// لوگوی هر اداره در public/logos/organizations
{ value: "water", label: "آب", logo: "water" },
{ value: "power", label: "برق", logo: "power" },
{ value: "gas", label: "گاز", logo: "gas" },
{ value: "phone", label: "تلفن", logo: "telecom" },
]
export default function BillPaymentBlock() {
const [type, setType] = React.useState("power")
const [billId, setBillId] = React.useState("")
const [paymentId, setPaymentId] = React.useState("")
const [amount, setAmount] = React.useState<number | null>(null)
const [loading, setLoading] = React.useState(false)
async function lookup(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
setLoading(true)
// مبلغ از شناسهٔ پرداخت درمیآید؛ این نمونه یک مبلغِ ثابت برمیگرداند.
await new Promise((resolve) => setTimeout(resolve, 700))
setLoading(false)
setAmount(386_000)
}
return (
<ControlSizeProvider size="xl" asChild>
<main className="flex min-h-svh items-start justify-center bg-muted p-4 sm:items-center">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle asChild>
<h1>پرداخت قبض</h1>
</CardTitle>
<CardDescription>شناسهها روی قبض کاغذی یا پیامک قبض آمدهاند.</CardDescription>
</CardHeader>
<ControlSizeProvider size="xl">
<form onSubmit={lookup}>
<CardContent className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<Label id="bill-type">نوع قبض</Label>
<ToggleGroup
type="single"
variant="segmented"
fullWidth
value={type}
onValueChange={(value) => value && setType(value as string)}
aria-labelledby="bill-type"
>
{BILL_TYPES.map((item) => (
<ToggleGroupItem key={item.value} value={item.value} className="gap-2">
{/* eslint-disable-next-line @next/next/no-img-element -- لوگوی کوچکِ ثابت */}
<img src={`/logos/organizations/${item.logo}.svg`} alt="" className="size-5" />
{item.label}
</ToggleGroupItem>
))}
</ToggleGroup>
</div>
<TextField
label="شناسهٔ قبض"
name="billId"
inputMode="numeric"
dir="ltr"
maxLength={13}
value={billId}
onValueChange={(value) => {
setBillId(value)
setAmount(null)
}}
required
/>
<TextField
label="شناسهٔ پرداخت"
name="paymentId"
inputMode="numeric"
dir="ltr"
maxLength={13}
value={paymentId}
onValueChange={(value) => {
setPaymentId(value)
setAmount(null)
}}
required
/>
{amount !== null && (
<>
<Separator />
<div className="flex items-center justify-between gap-2">
<span className="text-sm text-muted-foreground">مبلغ قابل پرداخت</span>
<span className="text-lg font-semibold">{formatCurrency(amount)}</span>
</div>
</>
)}
</CardContent>
<CardFooter>
{amount === null ? (
<Button type="submit" fullWidth loading={loading} disabled={billId.length < 6 || paymentId.length < 6}>
استعلام مبلغ
</Button>
) : (
<Button type="button" fullWidth onClick={() => toast.success("قبض پرداخت شد.")}>
پرداخت {formatCurrency(amount)}
</Button>
)}
</CardFooter>
</form>
</ControlSizeProvider>
</Card>
</main>
</ControlSizeProvider>
)
}