جدول در موبایل
پیشنمایش تمامصفحهردیفهای جدول به شکل کارت در موبایل و جدولِ کامل در تبلت، با باتمشیتِ عملیاتِ هر ردیف.
۱. نصب کامپوننتها
این بلاک از ۱۱ آیتم دیگ استفاده میکند: app-baravatarbadgebuttoncontrol-sizedrawericonspersiantabletoasttoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/app-bar.json https://docs.digdesign.ir/r/avatar.json 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/drawer.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/app-bar.json https://docs.digdesign.ir/r/avatar.json 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/drawer.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/app-bar.json https://docs.digdesign.ir/r/avatar.json 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/drawer.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/app-bar.json https://docs.digdesign.ir/r/avatar.json 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/drawer.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 بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
mobile-orders.tsx
"use client"
import * as React from "react"
import { ChevronRightIcon, MoreHorizontalIcon } from "@/components/icons"
import { AppBar, AppBarLeading, AppBarTitle } from "@/components/ui/app-bar"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Avatar } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerHeader,
DrawerTitle,
} from "@/components/ui/drawer"
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, formatJalali, toFaDigits } from "@/lib/persian"
import { stockImages, type StockImageName } from "@/lib/stock-images"
type Status = "processing" | "shipped" | "delivered" | "canceled"
const STATUS: Record<Status, { text: string; color: "info" | "warning" | "success" | "destructive" }> = {
processing: { text: "در حال آمادهسازی", color: "info" },
shipped: { text: "ارسال شد", color: "warning" },
delivered: { text: "تحویل شد", color: "success" },
canceled: { text: "لغو شد", color: "destructive" },
}
type Order = { id: string; customer: string; image: StockImageName; date: string; items: number; total: number; status: Status }
const ORDERS: Order[] = [
{ id: "1098", customer: "آرمیتا رستگار", image: "personWoman1", date: "2026-10-01", items: 2, total: 2_450_000, status: "shipped" },
{ id: "1097", customer: "کیان مرادی", image: "personMan1", date: "2026-09-30", items: 1, total: 1_265_000, status: "processing" },
{ id: "1096", customer: "نیلوفر احمدی", image: "personWoman3", date: "2026-09-29", items: 4, total: 5_100_000, status: "delivered" },
{ id: "1095", customer: "سارا محمدی", image: "personWoman2", date: "2026-09-28", items: 1, total: 789_000, status: "canceled" },
]
/**
* جدول در موبایل: هر ردیف یک کارت است با مهمترین ستونها (شماره، مشتری، مبلغ، وضعیت)؛
* از sm به بالا همان جدولِ کامل. عملیاتِ هر ردیف در باتمشیت باز میشود.
*/
export default function MobileOrdersBlock() {
const [filter, setFilter] = React.useState("all")
const [active, setActive] = React.useState<Order | null>(null)
const rows = ORDERS.filter((order) => filter === "all" || order.status === filter)
return (
<ControlSizeProvider size="xl" asChild>
<div className="mx-auto flex min-h-svh w-full max-w-3xl flex-col bg-background">
<AppBar sticky>
<AppBarLeading>
<Button variant="ghost" size="icon" aria-label="بازگشت">
<ChevronRightIcon className="ltr:rotate-180" />
</Button>
</AppBarLeading>
<AppBarTitle>سفارشها</AppBarTitle>
</AppBar>
<main className="flex flex-1 flex-col gap-6 p-4">
<ToggleGroup type="single" variant="segmented" value={filter} onValueChange={(value) => value && setFilter(value as string)} aria-label="فیلترِ وضعیت" className="w-full">
{[
{ value: "all", label: "همه" },
{ value: "processing", label: "جاری" },
{ value: "delivered", label: "تحویلشده" },
{ value: "canceled", label: "لغوشده" },
].map((item) => (
<ToggleGroupItem key={item.value} value={item.value} className="flex-1">
{item.label}
</ToggleGroupItem>
))}
</ToggleGroup>
{/* موبایل: کارت */}
<ul className="flex flex-col gap-3 sm:hidden">
{rows.map((order) => (
<li key={order.id} className="flex flex-col gap-3 rounded-xl border border-border p-4">
<div className="flex items-start gap-3">
<Avatar alt={order.customer} src={stockImages[order.image].src} />
<div className="flex flex-1 flex-col gap-1">
<span className="font-semibold">
سفارش <bdi dir="ltr">#{toFaDigits(order.id)}</bdi>
</span>
<span className="text-sm text-muted-foreground">
{order.customer} · {formatJalali(new Date(order.date))}
</span>
</div>
<Button variant="ghost" size="icon" aria-label={`عملیات سفارش ${toFaDigits(order.id)}`} onClick={() => setActive(order)}>
<MoreHorizontalIcon />
</Button>
</div>
<div className="flex items-center justify-between gap-2">
<Badge variant="soft" color={STATUS[order.status].color}>
{STATUS[order.status].text}
</Badge>
<span className="font-bold tabular-nums">{formatCurrency(order.total)}</span>
</div>
</li>
))}
</ul>
{/* تبلت و دسکتاپ: جدول */}
<div className="hidden overflow-hidden rounded-lg border border-border sm:block">
<Table>
<TableHeader className="bg-subtle">
<TableRow>
<TableHead>شماره</TableHead>
<TableHead>مشتری</TableHead>
<TableHead>تاریخ</TableHead>
<TableHead>تعداد</TableHead>
<TableHead className="text-end">مبلغ</TableHead>
<TableHead>وضعیت</TableHead>
<TableHead className="w-12">
<span className="sr-only">عملیات</span>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((order) => (
<TableRow key={order.id}>
<TableCell className="font-medium">
<bdi dir="ltr">#{toFaDigits(order.id)}</bdi>
</TableCell>
<TableCell>
<span className="flex items-center gap-2">
<Avatar size="sm" alt={order.customer} src={stockImages[order.image].src} />
{order.customer}
</span>
</TableCell>
<TableCell className="text-muted-foreground">{formatJalali(new Date(order.date))}</TableCell>
<TableCell>{toFaDigits(order.items)}</TableCell>
<TableCell className="text-end">{formatCurrency(order.total)}</TableCell>
<TableCell>
<Badge variant="soft" color={STATUS[order.status].color}>
{STATUS[order.status].text}
</Badge>
</TableCell>
<TableCell>
<Button variant="ghost" size="icon" aria-label={`عملیات سفارش ${toFaDigits(order.id)}`} onClick={() => setActive(order)}>
<MoreHorizontalIcon />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</main>
<Drawer open={active !== null} onOpenChange={(open) => !open && setActive(null)}>
<ControlSizeProvider size="xl" asChild>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>
سفارش <bdi dir="ltr">#{active && toFaDigits(active.id)}</bdi>
</DrawerTitle>
<DrawerDescription>
{active?.customer} · {active && formatCurrency(active.total)}
</DrawerDescription>
</DrawerHeader>
<div className="flex flex-col gap-2 p-4 pt-0">
<DrawerClose asChild>
<Button variant="outline" fullWidth>
مشاهدهٔ جزئیات
</Button>
</DrawerClose>
<DrawerClose asChild>
<Button variant="outline" fullWidth onClick={() => toast.success("فاکتور برای مشتری ارسال شد.")}>
ارسال فاکتور
</Button>
</DrawerClose>
<DrawerClose asChild>
<Button
variant="outline"
color="destructive"
fullWidth
disabled={active?.status === "delivered" || active?.status === "canceled"}
onClick={() => toast.warning("سفارش لغو شد.")}
>
لغو سفارش
</Button>
</DrawerClose>
</div>
</DrawerContent>
</ControlSizeProvider>
</Drawer>
</div>
</ControlSizeProvider>
)
}