بلاک‌ها · موبایل

جدول در موبایل

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

ردیف‌های جدول به شکل کارت در موبایل و جدولِ کامل در تبلت، با باتم‌شیتِ عملیاتِ هر ردیف.

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

این بلاک از ۱۱ آیتم دیگ استفاده می‌کند: 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.json
pnpm 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.json
yarn 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.json
bunx --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>
  )
}