بلاک‌ها · پنل مدیریت

میزِ کارِ سفارش‌ها

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

چهار کارتِ وضعیت با به‌روزکردن، نمودارِ فروشِ ماهانه، سهمِ کانال‌ها روی دونات، بنرِ طراحی‌شدهٔ گزارشِ صندوق و آخرین فعالیت‌ها.

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

این بلاک از ۷ آیتم دیگ استفاده می‌کند: buttoncardchart-presetscontrol-sizeiconspersiantoast

پکیج‌منیجر پیش‌فرض 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/chart-presets.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
pnpm dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/chart-presets.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
yarn dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/chart-presets.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
bunx --bun digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.json https://docs.digdesign.ir/r/chart-presets.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 بگذارید و متن‌ها، داده و هندلرها را با محصول خودتان عوض کنید.

orders-ops.tsx
"use client"

import * as React from "react"

import { BarChartIcon, CheckIcon, ClockIcon, FileTextIcon, RefreshIcon, SettingsIcon, ShoppingCartIcon, TruckIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { AreaChart, PieChart } from "@/components/ui/chart-presets"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { toast } from "@/components/ui/toast"
import { formatNumber, toFaDigits } from "@/lib/persian"

const STATUS = [
  { id: "pending", label: "در انتظارِ تأیید", value: 31, icon: <ClockIcon />, tone: "amber" },
  { id: "shipping", label: "در حالِ ارسال", value: 10, icon: <TruckIcon />, tone: "blue" },
  { id: "takeaway", label: "بیرون‌بر", value: 5630, icon: <ShoppingCartIcon />, tone: "pink" },
  { id: "all", label: "همهٔ سفارش‌ها", value: 1_632_000, icon: <CheckIcon />, tone: "green" },
] as const
const TONE = {
  amber: "bg-[color-mix(in_oklab,var(--dig-amber-500)_16%,var(--card))] text-[var(--dig-amber-700)] dark:text-[var(--dig-amber-300)]",
  blue: "bg-[color-mix(in_oklab,var(--dig-blue-500)_14%,var(--card))] text-[var(--dig-blue-600)] dark:text-[var(--dig-blue-300)]",
  pink: "bg-[color-mix(in_oklab,var(--dig-pink-500)_14%,var(--card))] text-[var(--dig-pink-600)] dark:text-[var(--dig-pink-300)]",
  green: "bg-[color-mix(in_oklab,var(--dig-green-500)_14%,var(--card))] text-[var(--dig-green-700)] dark:text-[var(--dig-green-300)]",
}
const SALES = [
  { label: "فروردین", value: 32 },
  { label: "اردیبهشت", value: 36 },
  { label: "خرداد", value: 31 },
  { label: "تیر", value: 41 },
  { label: "مرداد", value: 44 },
  { label: "شهریور", value: 38 },
  { label: "مهر", value: 47 },
  { label: "آبان", value: 52 },
]
const CHANNELS = [
  { name: "سالن", value: 2500 },
  { name: "بیرون‌بر", value: 1235 },
  { name: "پیک", value: 2658 },
  { name: "سایت", value: 380 },
]
const ACTIVITY = [
  { icon: <FileTextIcon />, title: "گزارشِ روزانه برای امورِ مالی ارسال شد", time: "۱۷ شهریور، ۱۴:۲۰" },
  { icon: <BarChartIcon />, title: "آمارِ فروشِ ماهانهٔ فروردین آماده شد", time: "۱۷ شهریور، ۱۲:۰۵" },
  { icon: <SettingsIcon />, title: "تنظیماتِ عمومیِ پنل به‌روز شد", time: "۱۶ شهریور، ۱۸:۴۰" },
]

/**
 * پنلِ عملیاتِ سفارش (رستوران یا فروشگاه): چهار کارتِ وضعیت با آیکونِ رنگی، شمار و «جزئیات» و به‌روزکردنِ
 * هر کارت، نمودارِ فروشِ ماهانه، سهمِ کانال‌ها روی دونات، آخرین فعالیت‌ها، و بنرِ طراحی‌شدهٔ «گزارشِ
 * صندوقِ روزانه» با زمانِ آخرین گزارش و یک اقدام.
 */
export default function OrdersOpsBlock() {
  const [refreshing, setRefreshing] = React.useState<string | null>(null)

  return (
    <ControlSizeProvider size="xl" asChild>
      <main className="mx-auto flex w-full max-w-6xl flex-col gap-6 p-4 sm:p-6">
        <header className="flex flex-wrap items-end justify-between gap-x-4 gap-y-3">
          <div className="flex flex-col gap-1">
            <h1 className="text-2xl leading-snug font-black">میزِ کار</h1>
            <p className="text-muted-foreground">پنجشنبه ۱۳ تیر · ۱۵:۳۲</p>
          </div>
        </header>

        <section aria-label="وضعیتِ سفارش‌ها" className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
          {STATUS.map((item) => (
            <Card key={item.id} className="gap-4 py-5">
              <CardContent className="flex items-start justify-between gap-3 px-5">
                <span className="flex flex-col gap-2">
                  <span className="text-sm font-semibold text-muted-foreground">{item.label}</span>
                  <span className="text-2xl font-black tabular-nums">{formatNumber(item.value)}</span>
                </span>
                <span aria-hidden className={`flex size-14 shrink-0 items-center justify-center rounded-2xl [&>svg]:size-7 ${TONE[item.tone]}`}>
                  {item.icon}
                </span>
              </CardContent>
              <div className="flex items-center justify-between gap-2 px-5">
                <ControlSizeProvider size="lg" asChild>
                  <Button variant="secondary" className="rounded-full px-4 text-sm">
                    جزئیات
                  </Button>
                </ControlSizeProvider>
                <ControlSizeProvider size="lg" asChild>
                  <Button
                    variant="ghost"
                    size="icon"
                    aria-label={`به‌روزکردنِ ${item.label}`}
                    loading={refreshing === item.id}
                    onClick={async () => {
                      setRefreshing(item.id)
                      await new Promise((resolve) => setTimeout(resolve, 600))
                      setRefreshing(null)
                    }}
                  >
                    <RefreshIcon />
                  </Button>
                </ControlSizeProvider>
              </div>
            </Card>
          ))}
        </section>

        <div className="grid grid-cols-1 items-start gap-6 lg:grid-cols-[minmax(0,1fr)_20rem]">
          <div className="flex min-w-0 flex-col gap-6">
            <Card>
              <CardHeader>
                <CardTitle className="text-lg">فروشِ ماهانه (میلیون تومان)</CardTitle>
              </CardHeader>
              <CardContent>
                <AreaChart className="h-64" data={SALES} categoryKey="label" smooth series={[{ key: "value", label: "فروش", color: "var(--dig-orange-500)" }]} valueFormatter={(value) => `${formatNumber(value)} میلیون`} />
              </CardContent>
            </Card>
            <Card>
              <CardHeader>
                <CardTitle className="text-lg">سهمِ کانال‌ها</CardTitle>
                <CardAction>
                  <span className="text-sm text-muted-foreground">{formatNumber(CHANNELS.reduce((sum, item) => sum + item.value, 0))} سفارش</span>
                </CardAction>
              </CardHeader>
              <CardContent>
                <PieChart className="h-64" data={CHANNELS} nameKey="name" valueKey="value" donut showLegend valueFormatter={(value) => `${formatNumber(value)} سفارش`} />
              </CardContent>
            </Card>
          </div>

          <div className="flex flex-col gap-6">
            {/* بنرِ گزارشِ صندوق: گرادیانِ گرم و پترن؛ اقدامِ روزانهٔ اصلی. */}
            <section aria-labelledby="cash-report" className="relative isolate flex flex-col gap-4 overflow-hidden rounded-xl bg-[linear-gradient(135deg,var(--dig-orange-600),var(--dig-pink-700))] p-6 text-[var(--dig-white)]">
              <span aria-hidden className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(color-mix(in_oklab,var(--dig-white)_30%,transparent)_1.2px,transparent_1.3px)] [background-size:16px_16px] [mask-image:linear-gradient(to_bottom,black,transparent_75%)]" />
              <span aria-hidden className="pointer-events-none absolute -end-10 -bottom-14 -z-10 size-40 rounded-full border-[20px] border-[color-mix(in_oklab,var(--dig-white)_12%,transparent)]" />
              <span aria-hidden className="flex size-12 items-center justify-center rounded-full bg-[var(--dig-white)] text-[var(--dig-orange-600)] [&>svg]:size-6">
                <CheckIcon />
              </span>
              <div className="flex flex-col gap-1">
                <h2 id="cash-report" className="text-lg font-black">
                  گزارشِ صندوقِ روزانه
                </h2>
                <p className="text-sm text-[var(--dig-orange-50)]">آخرین گزارش {toFaDigits(2)} ساعت پیش گرفته شد.</p>
              </div>
              <Button onClick={() => toast.success("گزارشِ صندوق آماده شد.")} className="bg-[var(--dig-white)] text-[var(--dig-orange-700)] hover:bg-[var(--dig-orange-50)]">
                گرفتنِ گزارش
              </Button>
            </section>

            <Card>
              <CardHeader>
                <CardTitle className="text-lg">آخرین فعالیت‌ها</CardTitle>
              </CardHeader>
              <CardContent>
                <ul className="flex flex-col gap-4">
                  {ACTIVITY.map((item) => (
                    <li key={item.title} className="flex items-start gap-3">
                      <span aria-hidden className="flex size-10 shrink-0 items-center justify-center rounded-full bg-muted [&>svg]:size-5">
                        {item.icon}
                      </span>
                      <span className="flex flex-col gap-0.5">
                        <span className="text-sm leading-6 font-medium">{item.title}</span>
                        <span className="text-xs text-muted-foreground">{item.time}</span>
                      </span>
                    </li>
                  ))}
                </ul>
              </CardContent>
            </Card>
          </div>
        </div>
      </main>
    </ControlSizeProvider>
  )
}