میزِ کارِ سفارشها
پیشنمایش تمامصفحهچهار کارتِ وضعیت با بهروزکردن، نمودارِ فروشِ ماهانه، سهمِ کانالها روی دونات، بنرِ طراحیشدهٔ گزارشِ صندوق و آخرین فعالیتها.
۱. نصب کامپوننتها
این بلاک از ۷ آیتم دیگ استفاده میکند: 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.jsonpnpm 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.jsonyarn 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.jsonbunx --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>
)
}