"use client"; import { BarChart3, Bell, Box, ChevronDown, ChevronLeft, ChevronRight, ChevronsUpDown, ClipboardList, FileText, Globe, LayoutDashboard, LineChart, LogOut, Menu, MessageSquare, Minus, Package, RefreshCw, RotateCcw, Search, Settings, ShoppingCart, Star, Tag, TrendingDown, TrendingUp, Truck, User, Users, } from "lucide-react"; import * as React from "react"; import type { TooltipProps } from "recharts"; import { Bar, BarChart, CartesianGrid, Scatter, ScatterChart, Tooltip, XAxis, YAxis, ZAxis, } from "recharts"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { Card, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; import { type ChartConfig, ChartContainer } from "@/components/ui/chart"; import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { cn } from "@/lib/utils"; type NavItem = { label: string; icon: React.ComponentType>; href: string; isActive?: boolean; children?: NavItem[]; }; type NavGroup = { title: string; items: NavItem[]; }; type UserData = { name: string; email: string; avatar: string; }; type ShellData = { logo: { src: string; alt: string; title: string; }; navGroups: NavGroup[]; footerGroup: NavGroup; user: UserData; }; type RichMetricStatItem = { title: string; value: number; trendValue: number; footerDelta: number; footerSubtextCount: number; }; type DailyMetric = { day: string; orderCount: number; avgOrderValue: number; totalRevenue: number; }; type ChannelRevenue = { month: string; online: number; inStore: number; wholesale: number; marketplace: number; }; type ActivityEvent = { id: string; type: "order" | "refund" | "stock" | "review" | "shipment"; message: string; timestamp: Date; }; type OrderStatus = "Processing" | "Shipped" | "Delivered" | "Cancelled"; type Order = { id: string; orderNumber: string; customer: string; total: number; status: OrderStatus; date: string; }; const currencyFormatter = new Intl.NumberFormat("fa-IR", { style: "currency", currency: "IRR", }); const compactCurrencyFormatter = new Intl.NumberFormat("fa-IR", { style: "currency", currency: "IRR", notation: "compact", maximumFractionDigits: 0, }); const numberFormatter = new Intl.NumberFormat("fa-IR"); const oneDecimalFormatter = new Intl.NumberFormat("fa-IR", { maximumFractionDigits: 1, }); const trendPercentFormatter = new Intl.NumberFormat("fa-IR", { style: "percent", maximumFractionDigits: 1, signDisplay: "exceptZero", }); const shortDateFormatter = new Intl.DateTimeFormat("fa-IR", { month: "short", day: "numeric", }); const timeFormatter = new Intl.DateTimeFormat("fa-IR", { hour: "numeric", minute: "2-digit", }); const formatTrendValue = (trendValue: number) => { if (trendValue === 0) return "۰٪"; return trendPercentFormatter.format(trendValue / 100); }; const orderStatusLabels: Record = { Processing: "در حال پردازش", Shipped: "ارسال‌شده", Delivered: "تحویل‌شده", Cancelled: "لغوشده", }; const mixBase = "transparent"; const palette = { primary: "var(--primary)", secondary: { light: `color-mix(in oklch, var(--primary) 75%, ${mixBase})`, dark: `color-mix(in oklch, var(--primary) 85%, ${mixBase})`, }, tertiary: { light: `color-mix(in oklch, var(--primary) 55%, ${mixBase})`, dark: `color-mix(in oklch, var(--primary) 65%, ${mixBase})`, }, quaternary: { light: `color-mix(in oklch, var(--primary) 40%, ${mixBase})`, dark: `color-mix(in oklch, var(--primary) 45%, ${mixBase})`, }, }; const shellData: ShellData = { logo: { src: "/block-media/shadcnblocks/block/logos/NovaSales-logo.svg", alt: "فروشگاه آکمه", title: "ری یو آی Store", }, navGroups: [ { title: "داشبورد", items: [ { label: "نمای کلی", icon: LayoutDashboard, href: "#", isActive: true, }, { label: "بلادرنگ", icon: RefreshCw, href: "#" }, { label: "گزارش‌ها", icon: BarChart3, href: "#" }, ], }, { title: "سفارش‌ها", items: [ { label: "همه سفارش‌ها", icon: ClipboardList, href: "#" }, { label: "مرجوعی‌ها", icon: RotateCcw, href: "#" }, { label: "ارسال", icon: Truck, href: "#" }, { label: "تکمیل", icon: Package, href: "#" }, ], }, { title: "محصولات", items: [ { label: "کاتالوگ", icon: Box, href: "#", children: [ { label: "همه محصولات", icon: FileText, href: "#" }, { label: "دسته‌بندی‌ها", icon: FileText, href: "#" }, ], }, { label: "موجودی", icon: Package, href: "#" }, { label: "قیمت‌گذاری", icon: Tag, href: "#" }, ], }, { title: "مشتریان", items: [ { label: "همه مشتریان", icon: Users, href: "#" }, { label: "بخش‌ها", icon: BarChart3, href: "#" }, { label: "پیام‌ها", icon: MessageSquare, href: "#" }, ], }, { title: "تحلیل فروش", items: [ { label: "تحلیل فروش", icon: LineChart, href: "#" }, { label: "ترافیک", icon: Globe, href: "#" }, { label: "تبدیل", icon: TrendingUp, href: "#" }, ], }, ], footerGroup: { title: "تنظیمات", items: [{ label: "تنظیمات", icon: Settings, href: "#" }], }, user: { name: "علی رضایی", email: "john@acme.store", avatar: "/block-media/shadcnblocks/block/avatar-4.webp", }, }; const richMetricStats: RichMetricStatItem[] = [ { title: "درآمد کل", value: 1_284_350, trendValue: 8.2, footerDelta: 97_420, footerSubtextCount: 18_450, }, { title: "سفارش‌های امروز", value: 342, trendValue: 12.5, footerDelta: 38, footerSubtextCount: 342, }, { title: "میانگین ارزش سفارش", value: 127.45, trendValue: -2.3, footerDelta: -3.05, footerSubtextCount: 9_800, }, { title: "نرخ تبدیل", value: 3.42, trendValue: 0.8, footerDelta: 0.03, footerSubtextCount: 145_000, }, ]; const dailyMetrics: DailyMetric[] = [ { day: "Jan 1", orderCount: 142, avgOrderValue: 89, totalRevenue: 12638 }, { day: "Jan 2", orderCount: 198, avgOrderValue: 112, totalRevenue: 22176 }, { day: "Jan 3", orderCount: 167, avgOrderValue: 95, totalRevenue: 15865 }, { day: "Jan 4", orderCount: 245, avgOrderValue: 78, totalRevenue: 19110 }, { day: "Jan 5", orderCount: 289, avgOrderValue: 134, totalRevenue: 38726 }, { day: "Jan 6", orderCount: 112, avgOrderValue: 156, totalRevenue: 17472 }, { day: "Jan 7", orderCount: 178, avgOrderValue: 102, totalRevenue: 18156 }, { day: "Jan 8", orderCount: 234, avgOrderValue: 88, totalRevenue: 20592 }, { day: "Jan 9", orderCount: 156, avgOrderValue: 145, totalRevenue: 22620 }, { day: "Jan 10", orderCount: 267, avgOrderValue: 110, totalRevenue: 29370 }, { day: "Jan 11", orderCount: 189, avgOrderValue: 167, totalRevenue: 31563 }, { day: "Jan 12", orderCount: 145, avgOrderValue: 92, totalRevenue: 13340 }, { day: "Jan 13", orderCount: 278, avgOrderValue: 125, totalRevenue: 34750 }, { day: "Jan 14", orderCount: 201, avgOrderValue: 138, totalRevenue: 27738 }, { day: "Jan 15", orderCount: 134, avgOrderValue: 176, totalRevenue: 23584 }, { day: "Jan 16", orderCount: 256, avgOrderValue: 99, totalRevenue: 25344 }, { day: "Jan 17", orderCount: 187, avgOrderValue: 142, totalRevenue: 26554 }, { day: "Jan 18", orderCount: 223, avgOrderValue: 85, totalRevenue: 18955 }, { day: "Jan 19", orderCount: 165, avgOrderValue: 118, totalRevenue: 19470 }, { day: "Jan 20", orderCount: 298, avgOrderValue: 105, totalRevenue: 31290 }, { day: "Jan 21", orderCount: 176, avgOrderValue: 162, totalRevenue: 28512 }, { day: "Jan 22", orderCount: 210, avgOrderValue: 94, totalRevenue: 19740 }, { day: "Jan 23", orderCount: 243, avgOrderValue: 128, totalRevenue: 31104 }, { day: "Jan 24", orderCount: 159, avgOrderValue: 151, totalRevenue: 24009 }, { day: "Jan 25", orderCount: 281, avgOrderValue: 87, totalRevenue: 24447 }, { day: "Jan 26", orderCount: 195, avgOrderValue: 143, totalRevenue: 27885 }, { day: "Jan 27", orderCount: 128, avgOrderValue: 189, totalRevenue: 24192 }, { day: "Jan 28", orderCount: 267, avgOrderValue: 96, totalRevenue: 25632 }, { day: "Jan 29", orderCount: 214, avgOrderValue: 131, totalRevenue: 28034 }, { day: "Jan 30", orderCount: 183, avgOrderValue: 117, totalRevenue: 21411 }, ]; const dayLabelFormatter = new Intl.DateTimeFormat("fa-IR", { month: "short", day: "numeric", }); dailyMetrics.forEach((entry, index) => { entry.day = dayLabelFormatter.format(new Date(2025, 0, index + 1)); }); const monthFormatter = new Intl.DateTimeFormat("fa-IR", { month: "short" }); const monthLabel = (monthIndex: number) => monthFormatter.format(new Date(2025, monthIndex, 1)); const channelRevenueData: ChannelRevenue[] = [ { month: monthLabel(0), online: 42000, inStore: 18000, wholesale: 12000, marketplace: 8000, }, { month: monthLabel(1), online: 38000, inStore: 21000, wholesale: 14000, marketplace: 9500, }, { month: monthLabel(2), online: 51000, inStore: 19000, wholesale: 11000, marketplace: 7200, }, { month: monthLabel(3), online: 45000, inStore: 22000, wholesale: 15000, marketplace: 10800, }, { month: monthLabel(4), online: 58000, inStore: 24000, wholesale: 13000, marketplace: 11500, }, { month: monthLabel(5), online: 62000, inStore: 20000, wholesale: 16000, marketplace: 12300, }, ]; const GAP_OFFSET = 2500; const fullYearData = [ { quarter: "س۱ '۲۱", thisYear: 28000, lastYear: -18000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۲ '۲۱", thisYear: 35000, lastYear: -22000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۳ '۲۱", thisYear: 32000, lastYear: -20000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۱ '۲۲", thisYear: 42000, lastYear: -25000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۲ '۲۲", thisYear: 48000, lastYear: -28000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۳ '۲۲", thisYear: 45000, lastYear: -24000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۱ '۲۳", thisYear: 55000, lastYear: -30000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۲ '۲۳", thisYear: 62000, lastYear: -32000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۳ '۲۳", thisYear: 58000, lastYear: -27000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۱ '۲۴", thisYear: 65000, lastYear: -35000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۲ '۲۴", thisYear: 70000, lastYear: -33000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, { quarter: "س۳ '۲۴", thisYear: 68000, lastYear: -30000, gapUp: GAP_OFFSET, gapDown: -GAP_OFFSET, }, ]; const revenueDomainMax = Math.ceil( Math.max( ...fullYearData.map((entry) => Math.max( Math.abs(entry.thisYear) + GAP_OFFSET, Math.abs(entry.lastYear) + GAP_OFFSET, ), ), ) / 5000, ) * 5000; const activityEvents: ActivityEvent[] = [ { id: "1", type: "order", message: "سفارش جدید #ORD-4892 توسط مریم J. ثبت شد", timestamp: new Date(2025, 0, 31, 14, 32), }, { id: "2", type: "shipment", message: "سفارش #ORD-4888 با FedEx ارسال شد", timestamp: new Date(2025, 0, 31, 13, 15), }, { id: "3", type: "review", message: "امتیاز ۵ ستاره برای Wireless Headphones حرفه‌ای", timestamp: new Date(2025, 0, 31, 12, 48), }, { id: "4", type: "refund", message: "بازپرداخت سفارش #ORD-4870 انجام شد", timestamp: new Date(2025, 0, 31, 11, 22), }, { id: "5", type: "stock", message: "هشدار موجودی کم: USB-C Charger (۱۲ عدد)", timestamp: new Date(2025, 0, 31, 10, 55), }, { id: "6", type: "order", message: "سفارش جدید #ORD-4891 توسط Michael C. ثبت شد", timestamp: new Date(2025, 0, 31, 10, 10), }, { id: "7", type: "shipment", message: "سفارش #ORD-4885 با موفقیت تحویل شد", timestamp: new Date(2025, 0, 31, 9, 45), }, { id: "8", type: "review", message: "امتیاز ۴ ستاره برای Organic Cotton Tee", timestamp: new Date(2025, 0, 31, 9, 20), }, { id: "9", type: "stock", message: "هشدار موجودی کم: Yoga Mat Premium (۵ عدد)", timestamp: new Date(2025, 0, 31, 8, 30), }, { id: "10", type: "order", message: "سفارش جدید #ORD-4890 توسط Emma W. ثبت شد", timestamp: new Date(2025, 0, 31, 8, 5), }, { id: "11", type: "refund", message: "درخواست بازپرداخت برای سفارش #ORD-4862", timestamp: new Date(2025, 0, 31, 7, 40), }, { id: "12", type: "shipment", message: "سفارش #ORD-4882 در مسیر تحویل است", timestamp: new Date(2025, 0, 31, 7, 15), }, ]; const activityIcons: Record< ActivityEvent["type"], React.ComponentType> > = { order: ShoppingCart, refund: RotateCcw, stock: Package, review: Star, shipment: Truck, }; const activityColors: Record = { order: "text-emerald-600 bg-emerald-50 dark:text-emerald-400 dark:bg-emerald-900/30", refund: "text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-900/30", stock: "text-amber-600 bg-amber-50 dark:text-amber-400 dark:bg-amber-900/30", review: "text-blue-600 bg-blue-50 dark:text-blue-400 dark:bg-blue-900/30", shipment: "text-violet-600 bg-violet-50 dark:text-violet-400 dark:bg-violet-900/30", }; const orderStatuses: OrderStatus[] = [ "Processing", "Shipped", "Delivered", "Cancelled", ]; const statusStyles: Record = { Processing: "bg-amber-50 text-amber-700 ring-1 ring-inset ring-amber-600/20 dark:bg-amber-900/30 dark:text-amber-400 dark:ring-amber-400/20", Shipped: "bg-blue-50 text-blue-700 ring-1 ring-inset ring-blue-700/10 dark:bg-blue-900/30 dark:text-blue-400 dark:ring-blue-400/20", Delivered: "bg-emerald-50 text-emerald-700 ring-1 ring-inset ring-emerald-600/20 dark:bg-emerald-900/30 dark:text-emerald-400 dark:ring-emerald-400/20", Cancelled: "bg-red-50 text-red-700 ring-1 ring-inset ring-red-600/10 dark:bg-red-900/30 dark:text-red-400 dark:ring-red-400/20", }; const orders: Order[] = [ { id: "1", orderNumber: "ORD-2025-001", customer: "Sarah Johnson", status: "Delivered", total: 2499.0, date: "2025-01-31", }, { id: "2", orderNumber: "ORD-2025-002", customer: "Michael Chen", status: "Shipped", total: 1348.0, date: "2025-01-30", }, { id: "3", orderNumber: "ORD-2025-003", customer: "Emma Wilson", status: "Processing", total: 1198.0, date: "2025-01-30", }, { id: "4", orderNumber: "ORD-2025-004", customer: "James Rodriguez", status: "Delivered", total: 799.0, date: "2025-01-29", }, { id: "5", orderNumber: "ORD-2025-005", customer: "Lisa Park", status: "Cancelled", total: 599.0, date: "2025-01-29", }, { id: "6", orderNumber: "ORD-2025-006", customer: "David Kim", status: "Shipped", total: 5498.0, date: "2025-01-28", }, { id: "7", orderNumber: "ORD-2025-007", customer: "Anna Martinez", status: "Delivered", total: 1199.0, date: "2025-01-28", }, { id: "8", orderNumber: "ORD-2025-008", customer: "Robert Taylor", status: "Processing", total: 1128.0, date: "2025-01-27", }, { id: "9", orderNumber: "ORD-2025-009", customer: "Jennifer Lee", status: "Shipped", total: 449.0, date: "2025-01-27", }, { id: "10", orderNumber: "ORD-2025-010", customer: "William Brown", status: "Delivered", total: 2199.0, date: "2025-01-26", }, { id: "11", orderNumber: "ORD-2025-011", customer: "Sophia Davis", status: "Cancelled", total: 349.0, date: "2025-01-26", }, { id: "12", orderNumber: "ORD-2025-012", customer: "Daniel Garcia", status: "Processing", total: 899.0, date: "2025-01-25", }, { id: "13", orderNumber: "ORD-2025-013", customer: "Olivia White", status: "Delivered", total: 3250.0, date: "2025-01-25", }, { id: "14", orderNumber: "ORD-2025-014", customer: "Thomas Harris", status: "Shipped", total: 742.0, date: "2025-01-24", }, { id: "15", orderNumber: "ORD-2025-015", customer: "Rachel Clark", status: "Processing", total: 1875.0, date: "2025-01-24", }, { id: "16", orderNumber: "ORD-2025-016", customer: "Kevin Lewis", status: "Delivered", total: 564.0, date: "2025-01-23", }, { id: "17", orderNumber: "ORD-2025-017", customer: "Amanda Robinson", status: "Cancelled", total: 2100.0, date: "2025-01-23", }, { id: "18", orderNumber: "ORD-2025-018", customer: "Chris Walker", status: "Shipped", total: 1450.0, date: "2025-01-22", }, { id: "19", orderNumber: "ORD-2025-019", customer: "Natalie Young", status: "Delivered", total: 890.0, date: "2025-01-22", }, { id: "20", orderNumber: "ORD-2025-020", customer: "Brian Allen", status: "Processing", total: 3675.0, date: "2025-01-21", }, ]; const PAGE_SIZE_OPTIONS = [10, 25, 50]; const scatterChartConfig = { scatter: { label: "شاخص‌های روزانه", color: palette.primary }, } satisfies ChartConfig; const channelChartConfig = { online: { label: "آنلاین", color: palette.primary }, inStore: { label: "حضوری", theme: palette.secondary }, wholesale: { label: "عمده‌فروشی", theme: palette.tertiary }, marketplace: { label: "مارکت‌پلیس", theme: palette.quaternary }, } satisfies ChartConfig; const revenueChartConfig = { thisYear: { label: "امسال", color: palette.primary }, lastYear: { label: "سال قبل", theme: palette.secondary }, } satisfies ChartConfig; const chartHeaderClass = "flex h-14 items-center justify-between border-b px-4 sm:px-5"; const chartIconBtnClass = "size-7 sm:size-8"; const chartIconClass = "size-4 text-muted-foreground sm:size-[18px]"; const chartTitleClass = "text-sm font-medium text-pretty sm:text-base"; const tooltipLabelClass = "text-[10px] text-muted-foreground sm:text-xs"; const tooltipValueClass = "text-[10px] font-medium text-foreground sm:text-xs"; const NavDropdown = ({ group, isActive, onSelect, }: { group: NavGroup; isActive: boolean; onSelect: () => void; }) => { return ( ); }; const SubNav = ({ group, activeItem, onSelectItem, }: { group: NavGroup; activeItem: NavItem | null; onSelectItem: (item: NavItem) => void; }) => { return (
{group.items.map((item) => { const Icon = item.icon; const isActive = activeItem?.label === item.label; const hasChildren = item.children && item.children.length > 0; if (hasChildren) { return ( {item.children!.map((child) => { const ChildIcon = child.icon; return ( ); })} ); } return ( ); })}
); }; const MobileNav = ({ activeGroupIndex, onSelectGroup, }: { activeGroupIndex: number; onSelectGroup: (index: number) => void; }) => { return ( {shellData.logo.alt} {shellData.logo.title} ); }; const NavUser = ({ user }: { user: UserData }) => { return (

{user.name}

{user.email}

); }; const DashboardHeader = () => { return (

صبح بخیر، جیمز.

وضعیت فروشگاه شما در امروز:

); }; const StatCards = () => { return (
{richMetricStats.map((stat) => { const isPositive = stat.trendValue > 0; const isNeutral = stat.trendValue === 0; const Icon = isNeutral ? Minus : isPositive ? TrendingUp : TrendingDown; const trendClass = isNeutral ? "text-foreground bg-muted" : isPositive ? "text-success border-success/20 bg-success/10" : "text-destructive border-destructive/20 bg-destructive/10"; const formattedTrend = formatTrendValue(stat.trendValue); let formattedValue: string; if (stat.title === "نرخ تبدیل") { formattedValue = `${oneDecimalFormatter.format(stat.value)}٪`; } else if (stat.title === "سفارش‌های امروز") { formattedValue = numberFormatter.format(stat.value); } else { formattedValue = currencyFormatter.format(stat.value); } let footerLabel: string; if (stat.title === "نرخ تبدیل") { const deltaPrefix = stat.footerDelta > 0 ? "+" : ""; footerLabel = `${deltaPrefix}${oneDecimalFormatter.format(stat.footerDelta)}٪ نسبت به ماه قبل`; } else if (stat.title === "سفارش‌های امروز") { const deltaPrefix = stat.footerDelta > 0 ? "+" : ""; footerLabel = `${deltaPrefix}${numberFormatter.format(stat.footerDelta)} نسبت به دیروز`; } else { const deltaPrefix = stat.footerDelta > 0 ? "+" : stat.footerDelta < 0 ? "-" : ""; footerLabel = `${deltaPrefix}${currencyFormatter.format(Math.abs(stat.footerDelta))} نسبت به ماه قبل`; } const footerSubtext = `بر اساس ${numberFormatter.format(stat.footerSubtextCount)} ${stat.title === "نرخ تبدیل" ? "بازدید" : "سفارش"}`; return (
{stat.title} {formattedValue}
{footerLabel}
{footerSubtext}
); })}
); }; function ScatterTooltip({ active, payload }: any) { if (!active || !payload?.length) return null; const data = payload[0]?.payload as DailyMetric | undefined; if (!data) return null; return (

{data.day}

سفارش‌ها: {numberFormatter.format(data.orderCount)}
میانگین سفارش: {currencyFormatter.format(data.avgOrderValue)}
درآمد: {currencyFormatter.format(data.totalRevenue)}
); } const AOVScatterChart = () => { const totalOrders = dailyMetrics.reduce((s, d) => s + d.orderCount, 0); const totalRevenue = dailyMetrics.reduce((s, d) => s + d.totalRevenue, 0); const avgAOV = totalOrders === 0 ? 0 : totalRevenue / totalOrders; return (

میانگین سفارش در برابر حجم

۳۰ روز اخیر

{currencyFormatter.format(avgAOV)}

میانگین سفارش

{numberFormatter.format(totalOrders)}

مجموع سفارش‌ها

{compactCurrencyFormatter.format(totalRevenue)}

درآمد کل

currencyFormatter.format(v)} /> } cursor={{ strokeDasharray: "3 3" }} />
); }; function ChannelTooltip({ active, payload, label, }: any) { if (!active || !payload?.length) return null; const total = payload.reduce( (sum: number, entry: any) => sum + Number(entry.value ?? 0), 0, ); return (

{label}

{payload.map((entry: any) => { const dataKey = String(entry.dataKey ?? ""); const label = channelChartConfig[dataKey as keyof typeof channelChartConfig] ?.label ?? entry.name ?? dataKey; return (
{label}: {compactCurrencyFormatter.format(Number(entry.value))}
); })}
مجموع: {compactCurrencyFormatter.format(total)}
); } const ChannelRevenueChart = () => { return (

درآمد بر اساس کانال

{[ { label: "آنلاین", color: palette.primary }, { label: "حضوری", color: palette.secondary.light }, { label: "عمده‌فروشی", color: palette.tertiary.light }, { label: "مارکت‌پلیس", color: palette.quaternary.light }, ].map((item) => (
{item.label}
))}
compactCurrencyFormatter.format(v)} /> } cursor={{ fillOpacity: 0.05 }} />
); }; function RevenueTooltip({ active, payload, label, }: any) { if (!active || !payload?.length) return null; const visiblePayload = payload.filter( (entry: any) => entry.dataKey !== "gapUp" && entry.dataKey !== "gapDown", ); if (!visiblePayload.length) return null; return (

{label}

{visiblePayload.map((entry: any) => { const dataKey = String(entry.dataKey ?? ""); const cfgLabel = revenueChartConfig[dataKey as keyof typeof revenueChartConfig] ?.label ?? dataKey; return (
{cfgLabel}: {currencyFormatter.format(Math.abs(Number(entry.value)))}
); })}
); } const RevenueOverviewChart = () => { const totalThisYear = fullYearData.reduce( (sum, entry) => sum + entry.thisYear, 0, ); const totalLastYear = fullYearData.reduce( (sum, entry) => sum + Math.abs(entry.lastYear), 0, ); const changePercent = totalLastYear === 0 ? 0 : ((totalThisYear - totalLastYear) / totalLastYear) * 100; const isPositive = changePercent >= 0; return (

نمای کلی درآمد

{[ { label: "امسال", color: palette.primary }, { label: "سال قبل", color: palette.secondary.light }, ].map((item) => (
{item.label}
))}

{currencyFormatter.format(totalThisYear)}

{isPositive ? "+" : "-"} {Math.abs(changePercent).toLocaleString("fa-IR", { maximumFractionDigits: 1, })} ٪ نسبت به سال قبل

compactCurrencyFormatter.format(Math.abs(v)) } /> } cursor={{ fillOpacity: 0.05 }} /> {/* Invisible spacer pushes thisYear bars above zero line */} {/* Invisible spacer pushes lastYear bars below zero line */}
); }; const ActivityFeed = () => { return (

فعالیت تیم‌های اخیر

{activityEvents.map((event) => { const Icon = activityIcons[event.type]; const colorClass = activityColors[event.type]; return (

{event.message}

{shortDateFormatter.format(event.timestamp)},{" "} {timeFormatter.format(event.timestamp)}

); })}
); }; const RecentOrdersTable = () => { const [searchQuery, setSearchQuery] = React.useState(""); const [statusFilter, setStatusFilter] = React.useState( "all", ); const [currentPage, setCurrentPage] = React.useState(1); const [pageSize, setPageSize] = React.useState(10); const [isHydrated, setIsHydrated] = React.useState(false); // Hydrate from URL on mount React.useEffect(() => { if (typeof window === "undefined") return; const params = new URLSearchParams(window.location.search); const q = params.get("q"); if (q) setSearchQuery(q); const nextStatus = params.get("status"); if ( nextStatus && (nextStatus === "all" || orderStatuses.includes(nextStatus as OrderStatus)) ) { setStatusFilter(nextStatus as OrderStatus | "all"); } const nextPage = Number(params.get("page")); if (!Number.isNaN(nextPage) && nextPage > 0) { setCurrentPage(nextPage); } const nextPageSize = Number(params.get("pageSize")); if (PAGE_SIZE_OPTIONS.includes(nextPageSize)) { setPageSize(nextPageSize); } setIsHydrated(true); }, []); const filteredOrders = React.useMemo(() => { return orders.filter((order) => { const matchesSearch = order.customer.toLowerCase().includes(searchQuery.toLowerCase()) || order.orderNumber.toLowerCase().includes(searchQuery.toLowerCase()); const matchesStatus = statusFilter === "all" || order.status === statusFilter; return matchesSearch && matchesStatus; }); }, [searchQuery, statusFilter]); const totalPages = Math.max(1, Math.ceil(filteredOrders.length / pageSize)); const paginatedOrders = React.useMemo(() => { const startIndex = (currentPage - 1) * pageSize; return filteredOrders.slice(startIndex, startIndex + pageSize); }, [filteredOrders, currentPage, pageSize]); // Reset page when filters change React.useEffect(() => { setCurrentPage(1); }, [searchQuery, statusFilter, pageSize]); // Sync state to URL React.useEffect(() => { if (!isHydrated || typeof window === "undefined") return; const params = new URLSearchParams(window.location.search); if (searchQuery) { params.set("q", searchQuery); } else { params.delete("q"); } if (statusFilter !== "all") { params.set("status", statusFilter); } else { params.delete("status"); } if (currentPage > 1) { params.set("page", String(currentPage)); } else { params.delete("page"); } if (pageSize !== PAGE_SIZE_OPTIONS[0]) { params.set("pageSize", String(pageSize)); } else { params.delete("pageSize"); } const nextQuery = params.toString(); const nextUrl = nextQuery ? `${window.location.pathname}?${nextQuery}` : window.location.pathname; window.history.replaceState(null, "", nextUrl); }, [searchQuery, statusFilter, currentPage, pageSize, isHydrated]); const goToPage = (page: number) => { setCurrentPage(Math.max(1, Math.min(page, totalPages))); }; const startRow = filteredOrders.length ? (currentPage - 1) * pageSize + 1 : 0; const endRow = Math.min(currentPage * pageSize, filteredOrders.length); return (

سفارش‌های اخیر

{filteredOrders.length.toLocaleString("fa-IR")}
فیلتر بر اساس وضعیت setStatusFilter("all")} > همه وضعیت‌ها {orderStatuses.map((status) => ( setStatusFilter(status)} > {orderStatusLabels[status]} ))}
مرجع سفارش مشتری تاریخ مجموع وضعیت {paginatedOrders.length === 0 ? ( سفارشی یافت نشد. ) : ( paginatedOrders.map((order) => ( {order.orderNumber} {order.customer} {shortDateFormatter.format(new Date(order.date))} {currencyFormatter.format(order.total)} {orderStatusLabels[order.status]} )) )}
ردیف در هر صفحه: {filteredOrders.length === 0 ? "۰" : `${startRow.toLocaleString("fa-IR")}-${endRow.toLocaleString("fa-IR")}`}{" "} از {filteredOrders.length.toLocaleString("fa-IR")}
); }; const DashboardContent = () => { return (
); }; const getDefaultActiveItem = (group: NavGroup): NavItem | null => { return group.items.find((item) => item.isActive) || group.items[0] || null; }; const Dashboard10 = ({ className }: { className?: string }) => { const [activeGroupIndex, setActiveGroupIndex] = React.useState(0); const activeGroup = shellData.navGroups[activeGroupIndex]; const [activeItem, setActiveItem] = React.useState( getDefaultActiveItem(activeGroup), ); // Update activeItem when group changes React.useEffect(() => { setActiveItem(getDefaultActiveItem(activeGroup)); }, [activeGroupIndex]); return (
رفتن به محتوای اصلی
{shellData.logo.alt}
{shellData.logo.title}
); }; export { Dashboard10 };