کارتِ ارسال
پیشنمایش تمامصفحههر ارسالِ سفارش یک کارت: شماره با رفتن به جزئیات، نوارِ چهارتکهای مراحل، مبدأ و سرویس و وزن و تاریخ، و پانویسِ حملکننده با وضعیت، کدِ رهگیریِ قابلِ کپی و زمانِ رسیدن.
۱. نصب کامپوننتها
این بلاک از ۶ آیتم دیگ استفاده میکند: badgebuttoncontrol-sizeiconspersiantoast
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/badge.json 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/toast.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/badge.json 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/toast.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/badge.json 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/toast.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/badge.json 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/toast.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
order-tracking-fulfillment.tsx
"use client"
import * as React from "react"
import { CheckIcon, ChevronLeftIcon, ClockIcon, CopyIcon, TruckIcon } from "@/components/icons"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { toast } from "@/components/ui/toast"
import { toFaDigits } from "@/lib/persian"
const STAGES = ["آمادهسازی", "مسیریابی", "تحویل به حمل", "تحویل به شما"]
type Fulfillment = { id: string; stage: number; progress: number; carrier: string; carrierTone: string; tracking: string; eta: string; rows: [string, string][] }
const FULFILLMENTS: Fulfillment[] = [
{
id: "۰۰۲۵۲",
stage: 3,
progress: 35,
carrier: "پستِ پیشتاز",
carrierTone: "bg-[var(--dig-red-600)]",
tracking: "236597455501263",
eta: "۲۳ مهر",
rows: [
["مبدأ", "انبارِ تهران"],
["سرویس", "پیشتاز، ۲ تا ۳ روز"],
["وزن", "۱٫۸ کیلوگرم"],
["تاریخِ ارسال", "۲۱ مهر ۱۴۰۵"],
],
},
{
id: "۰۰۲۵۳",
stage: 4,
progress: 100,
carrier: "تیپاکس",
carrierTone: "bg-[var(--dig-orange-500)]",
tracking: "TPX-88213045",
eta: "تحویل شد، ۲۰ مهر",
rows: [
["مبدأ", "انبارِ اصفهان"],
["سرویس", "اکسپرس، ۱ روز"],
["وزن", "۰٫۶ کیلوگرم"],
["تاریخِ ارسال", "۱۹ مهر ۱۴۰۵"],
],
},
]
/** نوارِ چهارتکهای: هر مرحله یک تکه با آیکونِ تیک یا ساعت بالایش؛ تکهٔ جاری به اندازهٔ پیشرفتش پر میشود. */
function StageBar({ item }: { item: Fulfillment }) {
return (
<ol className="grid grid-cols-4 gap-2" aria-label="مراحلِ ارسال">
{STAGES.map((title, index) => {
const done = index < item.stage - 1 || item.progress === 100
const current = index === item.stage - 1 && !done
return (
<li key={title} aria-current={current ? "step" : undefined} className="flex flex-col justify-between gap-2">
<span className={`flex items-start gap-1.5 text-xs leading-5 sm:items-center sm:text-sm [&>svg]:mt-0.5 [&>svg]:size-4 [&>svg]:shrink-0 sm:[&>svg]:mt-0 ${done ? "font-semibold" : current ? "font-semibold" : "text-muted-foreground"}`}>
{done ? <CheckIcon aria-label="انجام شد" className="text-success" /> : <ClockIcon aria-hidden className={current ? "text-[var(--dig-amber-600)]" : ""} />}
<span className="min-w-0">{title}</span>
</span>
<span className="h-1.5 overflow-hidden rounded-full bg-muted">
<span className="block h-full rounded-full bg-success" style={{ width: done ? "100%" : current ? `${item.progress}%` : "0%" }} />
</span>
</li>
)
})}
</ol>
)
}
/**
* کارتِ ارسال (fulfillment) برای پنل یا صفحهٔ سفارش: شمارهٔ ارسال با رفتن به جزئیات، نوارِ چهارتکهای
* مراحل، فهرستِ «برچسب ← مقدار» (مبدأ، سرویس، وزن، تاریخ)، و پانویس با حملکننده، وضعیت، کدِ رهگیریِ
* قابلِ کپی و زمانِ تخمینیِ رسیدن. چند ارسالِ یک سفارش زیرِ هم.
*/
export default function OrderTrackingFulfillmentBlock() {
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto flex w-full max-w-3xl flex-col gap-6 p-4 sm:p-6">
<header className="flex flex-col gap-1">
<h1 className="text-2xl leading-snug font-black">ارسالهای سفارشِ {toFaDigits(12567)}</h1>
<p className="text-muted-foreground">{toFaDigits(FULFILLMENTS.length)} بسته از دو انبار؛ هر کدام کدِ رهگیریِ خودش را دارد.</p>
</header>
{FULFILLMENTS.map((item) => {
const delivered = item.progress === 100
return (
<article key={item.id} className="overflow-hidden rounded-xl border border-border bg-card shadow-card">
<a href="#" className="flex items-center justify-between gap-3 border-b border-border px-5 py-4 hover:bg-subtle">
<span className="font-bold">
ارسالِ <bdi className="tabular-nums">#{item.id}</bdi>
</span>
<ChevronLeftIcon aria-hidden className="size-5 text-muted-foreground" />
<span className="sr-only">جزئیاتِ ارسال</span>
</a>
<div className="flex flex-col gap-5 px-5 py-5">
<StageBar item={item} />
<dl className="flex flex-col gap-3 text-sm">
{item.rows.map(([label, value]) => (
<div key={label} className="flex justify-between gap-4">
<dt className="text-muted-foreground">{label}</dt>
<dd className="font-medium">{value}</dd>
</div>
))}
</dl>
</div>
<footer className="flex flex-col gap-3 border-t border-border bg-subtle px-5 py-4 sm:flex-row sm:items-center sm:justify-between">
<span className="flex items-center gap-2">
<span aria-hidden className={`flex size-7 items-center justify-center rounded-md text-[var(--dig-white)] [&>svg]:size-4 ${item.carrierTone}`}>
<TruckIcon />
</span>
<span className="font-semibold">{item.carrier}</span>
<Badge variant="soft" color={delivered ? "success" : "info"}>
{delivered ? "تحویل شد" : "در راه"}
</Badge>
</span>
<span className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm">
<span className="flex items-center gap-1">
<span className="text-muted-foreground">رهگیری:</span>
<bdi dir="ltr" className="font-mono font-semibold">
{toFaDigits(item.tracking)}
</bdi>
<ControlSizeProvider size="lg" asChild>
<Button
variant="ghost"
size="icon"
aria-label="کپیِ کدِ رهگیری"
onClick={() => {
void navigator.clipboard?.writeText(item.tracking)
toast.success("کدِ رهگیری کپی شد.")
}}
>
<CopyIcon />
</Button>
</ControlSizeProvider>
</span>
<span>
<span className="text-muted-foreground">{delivered ? "" : "رسیدن: "}</span>
<span className="font-semibold">{item.eta}</span>
</span>
</span>
</footer>
</article>
)
})}
</main>
</ControlSizeProvider>
)
}