"use client"; import { ArrowUpDown, BarChart3, Bell, Box, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, ChevronsUpDown, ClipboardList, Download, Filter, Globe, Grid3x3, HelpCircle, LayoutDashboard, LogOut, MessageSquare, Minus, Package, RotateCcw, Search, Settings, TrendingDown, TrendingUp, Truck, User, Users, Wallet, X, } from "lucide-react"; import * as React from "react"; import type { TooltipProps } from "recharts"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, Cell, Pie, PieChart, PolarAngleAxis, PolarGrid, Radar, RadarChart, Sector, Tooltip, XAxis, YAxis, } from "recharts"; import type { PieSectorDataItem } from "recharts/types/polar/Pie"; 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, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarTrigger, } from "@/components/ui/sidebar"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Tooltip as ShadTooltip, TooltipContent as ShadTooltipContent, TooltipProvider as ShadTooltipProvider, TooltipTrigger as ShadTooltipTrigger, } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; type NavItem = { label: string; icon: React.ComponentType>; href: string; isActive?: boolean; children?: NavItem[]; }; type NavGroup = { title: string; items: NavItem[]; defaultOpen?: boolean; }; type UserData = { name: string; email: string; avatar: string; }; type SidebarData = { logo: { src: string; alt: string; title: string; description: string; }; navGroups: NavGroup[]; footerGroup: NavGroup; user?: UserData; }; type InvoicePriority = "High" | "Medium" | "Low"; type InvoiceStatus = "Paid" | "Pending" | "Overdue" | "Draft"; type Invoice = { id: string; invoiceId: string; customer: string; customerInitials: string; date: string; amount: number; priority: InvoicePriority; status: InvoiceStatus; }; type InvoiceSortKey = "customer" | "invoiceId" | "date" | "amount"; type RichMetricStatItem = { title: string; value: number; trendValue: number; footerDelta: number; footerSubtextCount: number; }; 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 compactCurrencyOneDecimalFormatter = new Intl.NumberFormat("fa-IR", { style: "currency", currency: "IRR", notation: "compact", maximumFractionDigits: 1, }); const invoiceDateFormatter = new Intl.DateTimeFormat("fa-IR", { year: "numeric", month: "short", day: "numeric", }); const numberFormatter = new Intl.NumberFormat("fa-IR"); const weekdayFormatter = new Intl.DateTimeFormat("fa-IR", { weekday: "short" }); const monthFormatter = new Intl.DateTimeFormat("fa-IR", { month: "short" }); const dashboardRangeFormatter = new Intl.DateTimeFormat("fa-IR", { month: "short", day: "numeric", year: "numeric", }); const dashboardRangeStart = new Date(2025, 0, 1); const dashboardRangeEnd = new Date(2025, 0, 31); const dashboardDateRangeLabel = typeof dashboardRangeFormatter.formatRange === "function" ? dashboardRangeFormatter.formatRange( dashboardRangeStart, dashboardRangeEnd, ) : `${dashboardRangeFormatter.format( dashboardRangeStart, )} – ${dashboardRangeFormatter.format(dashboardRangeEnd)}`; const invoiceStatusLabels: Record = { Paid: "پرداخت‌شده", Pending: "در انتظار", Overdue: "سررسید گذشته", Draft: "پیش‌نویس", }; const invoicePriorityLabels: Record = { High: "بالا", Medium: "متوسط", Low: "پایین", }; const mixBase = "var(--background)"; 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 sidebarData: SidebarData = { logo: { src: "/block-media/shadcnblocks/block/logos/NovaSales-logo.svg", alt: "فروشگاه آکمه", title: "ری یو آی Store", description: "فروشگاه آنلاین", }, navGroups: [ { title: "اصلی", defaultOpen: true, items: [ { label: "داشبورد", icon: LayoutDashboard, href: "#", isActive: true, }, { label: "سفارش‌ها", icon: ClipboardList, href: "#" }, { label: "مرجوعی‌ها", icon: RotateCcw, href: "#" }, ], }, { title: "کاتالوگ", defaultOpen: true, items: [ { label: "محصولات", icon: Box, href: "#", children: [ { label: "همه محصولات", icon: Package, href: "#" }, { label: "دسته‌بندی‌ها", icon: Package, href: "#" }, { label: "موجودی", icon: Package, href: "#" }, ], }, { label: "ارسال", icon: Truck, href: "#" }, ], }, { title: "مشتریان", defaultOpen: false, items: [ { label: "همه مشتریان", icon: Users, href: "#" }, { label: "پیام‌ها", icon: MessageSquare, href: "#" }, ], }, { title: "تحلیل فروش", defaultOpen: false, items: [ { label: "نمای کلی", icon: Globe, href: "#" }, { label: "گزارش‌ها", icon: BarChart3, href: "#" }, { label: "امور مالی", icon: Wallet, href: "#" }, ], }, ], footerGroup: { title: "تنظیمات", items: [{ label: "تنظیمات", icon: Settings, href: "#" }], }, user: { name: "علی رضایی", email: "john@acme.store", avatar: "/block-media/shadcnblocks/block/avatar-6.webp", }, }; const richMetricStats: RichMetricStatItem[] = [ { title: "فروش حضوری", value: 7820.75, trendValue: 4.3, footerDelta: 322.5, footerSubtextCount: 5000, }, { title: "فروش آنلاین", value: 985_937.45, trendValue: 12.5, footerDelta: 109_500, footerSubtextCount: 21_000, }, { title: "عمده‌فروشی", value: 124_650, trendValue: 6.8, footerDelta: 7_935, footerSubtextCount: 890, }, { title: "مرجوعی‌ها", value: 15_503, trendValue: -3.1, footerDelta: -498, footerSubtextCount: 6000, }, ]; const weeklyRevenueData = [ { day: weekdayFormatter.format(new Date(2025, 0, 6)), revenue: 1200, expenses: 800, }, { day: weekdayFormatter.format(new Date(2025, 0, 7)), revenue: 1500, expenses: 600, }, { day: weekdayFormatter.format(new Date(2025, 0, 8)), revenue: 1800, expenses: 1100, }, { day: weekdayFormatter.format(new Date(2025, 0, 9)), revenue: 1100, expenses: 900, }, { day: weekdayFormatter.format(new Date(2025, 0, 10)), revenue: 1600, expenses: 1200, }, { day: weekdayFormatter.format(new Date(2025, 0, 11)), revenue: 800, expenses: 400, }, { day: weekdayFormatter.format(new Date(2025, 0, 12)), revenue: 900, expenses: 500, }, ]; const revenueChartConfig = { revenue: { label: "درآمد", color: palette.primary }, expenses: { label: "هزینه", theme: palette.secondary }, } satisfies ChartConfig; const monthLabel = (monthIndex: number) => monthFormatter.format(new Date(2025, monthIndex, 1)); const salesPipelineData: Record< string, { week: string; month: string; orders: number; sales: number }[] > = { q1: [ { week: "W1", month: monthLabel(0), orders: 220, sales: 5100 }, { week: "W2", month: monthLabel(0), orders: 480, sales: 11200 }, { week: "W3", month: monthLabel(0), orders: 390, sales: 9400 }, { week: "W4", month: monthLabel(0), orders: 150, sales: 3600 }, { week: "W5", month: monthLabel(1), orders: 310, sales: 7400 }, { week: "W6", month: monthLabel(1), orders: 540, sales: 13100 }, { week: "W7", month: monthLabel(1), orders: 460, sales: 10800 }, { week: "W8", month: monthLabel(1), orders: 200, sales: 4700 }, { week: "W9", month: monthLabel(2), orders: 130, sales: 3100 }, { week: "W10", month: monthLabel(2), orders: 420, sales: 10200 }, { week: "W11", month: monthLabel(2), orders: 510, sales: 12400 }, { week: "W12", month: monthLabel(2), orders: 350, sales: 8500 }, ], q2: [ { week: "W1", month: monthLabel(3), orders: 410, sales: 9800 }, { week: "W2", month: monthLabel(3), orders: 280, sales: 6700 }, { week: "W3", month: monthLabel(3), orders: 120, sales: 2900 }, { week: "W4", month: monthLabel(3), orders: 350, sales: 8400 }, { week: "W5", month: monthLabel(4), orders: 520, sales: 12600 }, { week: "W6", month: monthLabel(4), orders: 470, sales: 11300 }, { week: "W7", month: monthLabel(4), orders: 190, sales: 4500 }, { week: "W8", month: monthLabel(4), orders: 100, sales: 2400 }, { week: "W9", month: monthLabel(5), orders: 330, sales: 7900 }, { week: "W10", month: monthLabel(5), orders: 490, sales: 11800 }, { week: "W11", month: monthLabel(5), orders: 540, sales: 13000 }, { week: "W12", month: monthLabel(5), orders: 260, sales: 6200 }, ], q3: [ { week: "W1", month: monthLabel(6), orders: 180, sales: 4200 }, { week: "W2", month: monthLabel(6), orders: 520, sales: 12800 }, { week: "W3", month: monthLabel(6), orders: 480, sales: 11500 }, { week: "W4", month: monthLabel(6), orders: 120, sales: 2800 }, { week: "W5", month: monthLabel(7), orders: 90, sales: 2100 }, { week: "W6", month: monthLabel(7), orders: 450, sales: 10500 }, { week: "W7", month: monthLabel(7), orders: 510, sales: 12200 }, { week: "W8", month: monthLabel(7), orders: 480, sales: 11000 }, { week: "W9", month: monthLabel(8), orders: 200, sales: 4800 }, { week: "W10", month: monthLabel(8), orders: 150, sales: 3500 }, { week: "W11", month: monthLabel(8), orders: 380, sales: 9200 }, { week: "W12", month: monthLabel(8), orders: 420, sales: 10100 }, ], q4: [ { week: "W1", month: monthLabel(9), orders: 300, sales: 7200 }, { week: "W2", month: monthLabel(9), orders: 160, sales: 3800 }, { week: "W3", month: monthLabel(9), orders: 440, sales: 10600 }, { week: "W4", month: monthLabel(9), orders: 530, sales: 12900 }, { week: "W5", month: monthLabel(10), orders: 380, sales: 9100 }, { week: "W6", month: monthLabel(10), orders: 140, sales: 3400 }, { week: "W7", month: monthLabel(10), orders: 250, sales: 6000 }, { week: "W8", month: monthLabel(10), orders: 500, sales: 12100 }, { week: "W9", month: monthLabel(11), orders: 550, sales: 13300 }, { week: "W10", month: monthLabel(11), orders: 470, sales: 11400 }, { week: "W11", month: monthLabel(11), orders: 210, sales: 5000 }, { week: "W12", month: monthLabel(11), orders: 340, sales: 8200 }, ], }; const pipelineOrdersConfig = { orders: { label: "سفارش‌ها", color: palette.primary }, } satisfies ChartConfig; const pipelineSalesConfig = { sales: { label: "فروش", theme: palette.tertiary }, } satisfies ChartConfig; const incomeCategoryData = [ { category: "الکترونیک", value: 8200 }, { category: "پوشاک", value: 5400 }, { category: "خانه و باغ", value: 3800 }, { category: "ورزشی", value: 4100 }, { category: "زیبایی", value: 2900 }, ]; const radarChartConfig = { value: { label: "فروش", color: palette.primary }, } satisfies ChartConfig; type HeatmapCell = { value: number; isCurrentWeek: boolean; }; type TrafficSource = { name: string; total: string; weeks: HeatmapCell[]; }; const trafficSourcesData: TrafficSource[] = [ { name: "", total: "6k", weeks: [ { value: 0, isCurrentWeek: false }, { value: 0, isCurrentWeek: false }, { value: 0, isCurrentWeek: false }, { value: 0, isCurrentWeek: false }, ], }, { name: "تبلیغات گوگل", total: "5k", weeks: [ { value: 0.5, isCurrentWeek: false }, { value: 0.95, isCurrentWeek: true }, { value: 0.2, isCurrentWeek: false }, { value: 0.55, isCurrentWeek: false }, ], }, { name: "فیسبوک", total: "4k", weeks: [ { value: 0.25, isCurrentWeek: false }, { value: 0.6, isCurrentWeek: false }, { value: 0.15, isCurrentWeek: false }, { value: 0.5, isCurrentWeek: false }, ], }, { name: "ارگانیک", total: "2k", weeks: [ { value: 0.2, isCurrentWeek: false }, { value: 0.9, isCurrentWeek: true }, { value: 0.1, isCurrentWeek: false }, { value: 0.15, isCurrentWeek: false }, ], }, { name: "ایمیل", total: "3k", weeks: [ { value: 0.45, isCurrentWeek: false }, { value: 0.95, isCurrentWeek: true }, { value: 0.85, isCurrentWeek: true }, { value: 0.2, isCurrentWeek: false }, ], }, { name: "مستقیم", total: "4.5k", weeks: [ { value: 0.4, isCurrentWeek: false }, { value: 0.9, isCurrentWeek: true }, { value: 0.8, isCurrentWeek: true }, { value: 0.25, isCurrentWeek: false }, ], }, ]; const weekLabels = ["هفته ۱", "هفته ۲", "هفته ۳", "هفته ۴"]; const orderStatIRRata = [ { key: "fulfilled", label: "تکمیل‌شده", value: 3200 }, { key: "pending", label: "در انتظار", value: 850 }, ]; const orderStatusChartConfig = { fulfilled: { label: "تکمیل‌شده", color: palette.primary }, pending: { label: "در انتظار", theme: palette.tertiary }, } satisfies ChartConfig; const invoiceStatuses: InvoiceStatus[] = [ "Paid", "Pending", "Overdue", "Draft", ]; const INVOICE_PAGE_SIZE_OPTIONS = [5, 10, 20]; const invoices: Invoice[] = [ { id: "1", invoiceId: "INV-00001", customer: "James Brown", customerInitials: "JB", date: "2025-01-05", amount: 1250.0, priority: "High", status: "Paid", }, { id: "2", invoiceId: "INV-00002", customer: "Sarah Miller", customerInitials: "SM", date: "2025-01-08", amount: 3420.5, priority: "Medium", status: "Pending", }, { id: "3", invoiceId: "INV-00003", customer: "Michael Chen", customerInitials: "MC", date: "2025-01-12", amount: 890.0, priority: "Low", status: "Paid", }, { id: "4", invoiceId: "INV-00004", customer: "Emily Davis", customerInitials: "ED", date: "2025-01-15", amount: 4750.0, priority: "High", status: "Overdue", }, { id: "5", invoiceId: "INV-00005", customer: "Robert Wilson", customerInitials: "RW", date: "2025-01-18", amount: 567.25, priority: "Low", status: "Draft", }, { id: "6", invoiceId: "INV-00006", customer: "Lisa Anderson", customerInitials: "LA", date: "2025-01-22", amount: 2100.0, priority: "Medium", status: "Paid", }, { id: "7", invoiceId: "INV-00007", customer: "David Martinez", customerInitials: "DM", date: "2025-02-01", amount: 1875.75, priority: "High", status: "Pending", }, { id: "8", invoiceId: "INV-00008", customer: "Jennifer Taylor", customerInitials: "JT", date: "2025-02-05", amount: 45.0, priority: "Low", status: "Paid", }, { id: "9", invoiceId: "INV-00009", customer: "Chris Thompson", customerInitials: "CT", date: "2025-02-10", amount: 3200.0, priority: "Medium", status: "Overdue", }, { id: "10", invoiceId: "INV-00010", customer: "Amanda White", customerInitials: "AW", date: "2025-02-14", amount: 980.5, priority: "High", status: "Pending", }, { id: "11", invoiceId: "INV-00011", customer: "Kevin Harris", customerInitials: "KH", date: "2025-02-18", amount: 4200.0, priority: "Medium", status: "Paid", }, { id: "12", invoiceId: "INV-00012", customer: "Rachel Clark", customerInitials: "RC", date: "2025-02-22", amount: 1560.0, priority: "Low", status: "Draft", }, { id: "13", invoiceId: "INV-00013", customer: "Thomas Lewis", customerInitials: "TL", date: "2025-03-01", amount: 2890.0, priority: "High", status: "Overdue", }, { id: "14", invoiceId: "INV-00014", customer: "Maria Robinson", customerInitials: "MR", date: "2025-03-05", amount: 725.0, priority: "Low", status: "Paid", }, { id: "15", invoiceId: "INV-00015", customer: "Daniel Walker", customerInitials: "DW", date: "2025-03-10", amount: 5000.0, priority: "High", status: "Pending", }, { id: "16", invoiceId: "INV-00016", customer: "Sophie Hall", customerInitials: "SH", date: "2025-03-14", amount: 1340.0, priority: "Medium", status: "Paid", }, { id: "17", invoiceId: "INV-00017", customer: "Nathan Young", customerInitials: "NY", date: "2025-03-18", amount: 2675.5, priority: "Medium", status: "Draft", }, { id: "18", invoiceId: "INV-00018", customer: "Olivia King", customerInitials: "OK", date: "2025-03-22", amount: 460.0, priority: "Low", status: "Pending", }, ]; const badgeClassName = "inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium ring-1 ring-inset"; const invoiceStatusStyles: Record = { Paid: "ring-emerald-500/30 text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10", Pending: "ring-amber-500/30 text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-500/10", Overdue: "ring-red-500/30 text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-500/10", Draft: "ring-zinc-500/30 text-zinc-600 dark:text-zinc-400 bg-zinc-100 dark:bg-zinc-500/10", }; const invoicePriorityStyles: Record = { High: "ring-red-500/30 text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-500/10", Medium: "ring-amber-500/30 text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-500/10", Low: "ring-zinc-500/30 text-zinc-600 dark:text-zinc-400 bg-zinc-100 dark:bg-zinc-500/10", }; const SidebarLogo = ({ logo }: { logo: SidebarData["logo"] }) => { return (
{logo.alt}
{logo.title} {logo.description}
); }; const NavMenuItem = ({ item }: { item: NavItem }) => { const Icon = item.icon; const hasChildren = item.children && item.children.length > 0; if (!hasChildren) { return ( ); } return ( {item.children!.map((child) => ( {child.label} ))} ); }; const NavUser = ({ user }: { user: UserData }) => { return ( {user.name .split(" ") .map((n) => n[0]) .join("")}
{user.name} {user.email}
{user.name .split(" ") .map((n) => n[0]) .join("")}
{user.name} {user.email}
); }; const AppSidebar = ({ ...props }: React.ComponentProps) => { return (
{sidebarData.navGroups.map((group) => ( {group.title} {group.items.map((item) => ( ))} ))} {sidebarData.user && }
); }; const DashboardHeader = () => { return (

نمای کلی داشبورد

خلاصه‌ای از خریدها، فروش‌ها و سایر شاخص‌ها

{dashboardDateRangeLabel}
); }; const AccountingStatsCards = () => { return (
{richMetricStats.map((stat) => { const isPositive = stat.trendValue > 0; const isNeutral = stat.trendValue === 0; const Icon = isNeutral ? Minus : isPositive ? TrendingUp : TrendingDown; const deltaPrefix = stat.footerDelta > 0 ? "+" : stat.footerDelta < 0 ? "-" : ""; const footerLabel = `${deltaPrefix}${currencyFormatter.format( Math.abs(stat.footerDelta), )} نسبت به ماه قبل`; const footerSubtext = `بر اساس ${numberFormatter.format( stat.footerSubtextCount, )} سفارش`; return ( {stat.title} {currencyFormatter.format(stat.value)}
{footerLabel}
{footerSubtext}
); })}
); }; const RevenueChartTooltip = ({ active, payload, label, }: any) => { if (!active || !payload?.length) return null; const revenue = payload.find((p: any) => p.dataKey === "revenue")?.value ?? 0; const expenses = payload.find((p: any) => p.dataKey === "expenses")?.value ?? 0; const total = Number(revenue) + Number(expenses); return (

{label}

درآمد: {currencyFormatter.format(Number(revenue))}
هزینه: {currencyFormatter.format(Number(expenses))}
مجموع: {currencyFormatter.format(total)}
); }; const RevenueChart = () => { const totalRevenue = weeklyRevenueData.reduce((sum, d) => sum + d.revenue, 0); return (

درآمد

درآمد
هزینه

{currencyFormatter.format(totalRevenue)}

این هفته در برابر هفته قبل

compactCurrencyFormatter.format(value) } width={40} /> } cursor={{ fillOpacity: 0.05 }} />
); }; const SalesPipelineChart = () => { const [searchParams, setSearchParams] = React.useState( () => new URLSearchParams( typeof window !== "undefined" ? window.location.search : "", ), ); const quarter = searchParams.get("quarter") ?? "q3"; const handleQuarterChange = (value: string) => { const next = new URLSearchParams(searchParams); next.set("quarter", value); setSearchParams(next); window.history.replaceState(null, "", `?${next.toString()}`); }; const data = salesPipelineData[quarter] ?? salesPipelineData.q3; const totalOrders = data.reduce((sum, d) => sum + d.orders, 0); const totalSales = data.reduce((sum, d) => sum + d.sales, 0); const pipelineLabelFormatter = ( value: React.ReactNode, payload: ReadonlyArray<{ payload?: { week?: string } }> | undefined, ) => { const week = payload?.[0]?.payload?.week; if (!week) return value; return `${value} · ${week}`; }; return (

قیف فروش

{numberFormatter.format(totalOrders)}

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

{ const numericValue = Number(value); const formatted = numberFormatter.format( Number.isFinite(numericValue) ? numericValue : 0, ); return (
سفارش‌ها {formatted}
); }} /> } />

{compactCurrencyOneDecimalFormatter.format(totalSales)}

مجموع فروش

{ const numericValue = Number(value); const formatted = currencyFormatter.format( Number.isFinite(numericValue) ? numericValue : 0, ); return (
فروش {formatted}
); }} /> } />
); }; const IncomeByCategoryChart = () => { return (

فروش بر اساس دسته

} />
); }; const TrafficSourcesHeatmap = () => { return (

منابع ترافیک

+30%
زیاد
متوسط
کم
{trafficSourcesData.map((source) => (
{source.total}
{source.weeks.map((cell, weekIndex) => { const intensity = cell.isCurrentWeek ? Math.round(cell.value * 30 + 65) : Math.round(cell.value * 35 + 15); const tooltipTitle = source.name || "کل"; const weekLabel = weekLabels[weekIndex] ?? `هفته ${(weekIndex + 1).toLocaleString("fa-IR")}`; const strength = Math.round(cell.value * 100); return (
); }; const OrderStatusChart = () => { const total = orderStatIRRata.reduce((sum, d) => sum + d.value, 0); const sliceColors = [palette.primary, palette.tertiary.light]; const [activeIndex, setActiveIndex] = React.useState(null); return (

وضعیت سفارش

} /> ( )} onMouseEnter={(_, index) => setActiveIndex(index)} onMouseLeave={() => setActiveIndex(null)} onClick={(_, index) => setActiveIndex((current) => (current === index ? null : index)) } > {orderStatIRRata.map((entry, index) => ( ))}
سفارش‌ها {numberFormatter.format(total)}
{orderStatIRRata.map((item, index) => ( ))}
); }; const RecentInvoicesTable = () => { const [searchQuery, setSearchQuery] = React.useState(""); const [statusFilter, setStatusFilter] = React.useState("all"); const [currentPage, setCurrentPage] = React.useState(1); const [pageSize, setPageSize] = React.useState(5); const [sortKey, setSortKey] = React.useState(""); const [sortDir, setSortDir] = React.useState<"asc" | "desc">("asc"); const [isHydrated, setIsHydrated] = React.useState(false); const skipNextReset = React.useRef(true); const hasActiveFilters = statusFilter !== "all"; const clearFilters = () => { setStatusFilter("all"); }; // Hydrate from URL params on mount React.useEffect(() => { if (typeof window === "undefined") return; const params = new URLSearchParams(window.location.search); const statusParam = params.get("invoiceStatus"); const pageParam = Number.parseInt(params.get("page") ?? "", 10); const pageSizeParam = Number.parseInt(params.get("pageSize") ?? "", 10); const sortKeyParam = params.get("sortKey") as InvoiceSortKey | null; const sortDirParam = params.get("sortDir"); setSearchQuery(params.get("q") ?? ""); setStatusFilter( statusParam && invoiceStatuses.includes(statusParam as InvoiceStatus) ? statusParam : "all", ); setCurrentPage(Number.isFinite(pageParam) && pageParam > 0 ? pageParam : 1); setPageSize( INVOICE_PAGE_SIZE_OPTIONS.includes(pageSizeParam) ? pageSizeParam : 5, ); if ( sortKeyParam && ["customer", "invoiceId", "date", "amount"].includes(sortKeyParam) ) { setSortKey(sortKeyParam); setSortDir(sortDirParam === "desc" ? "desc" : "asc"); } setIsHydrated(true); }, []); // Reset page to 1 when filters change React.useEffect(() => { if (!isHydrated) return; if (skipNextReset.current) { skipNextReset.current = false; return; } setCurrentPage(1); }, [searchQuery, statusFilter, pageSize, isHydrated]); // Sync state to URL React.useEffect(() => { if (!isHydrated) return; if (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("invoiceStatus", statusFilter); } else { params.delete("invoiceStatus"); } if (currentPage > 1) { params.set("page", String(currentPage)); } else { params.delete("page"); } if (pageSize !== INVOICE_PAGE_SIZE_OPTIONS[0]) { params.set("pageSize", String(pageSize)); } else { params.delete("pageSize"); } if (sortKey) { params.set("sortKey", sortKey); params.set("sortDir", sortDir); } else { params.delete("sortKey"); params.delete("sortDir"); } const nextQuery = params.toString(); const nextUrl = nextQuery ? `${window.location.pathname}?${nextQuery}` : window.location.pathname; window.history.replaceState(null, "", nextUrl); }, [ searchQuery, statusFilter, currentPage, pageSize, sortKey, sortDir, isHydrated, ]); const handleSort = (key: InvoiceSortKey) => { if (sortKey === key) { setSortDir((prev) => (prev === "asc" ? "desc" : "asc")); } else { setSortKey(key); setSortDir("asc"); } }; const filteredInvoices = React.useMemo(() => { return invoices.filter((invoice) => { const matchesSearch = invoice.customer.toLowerCase().includes(searchQuery.toLowerCase()) || invoice.invoiceId.toLowerCase().includes(searchQuery.toLowerCase()); const matchesStatus = statusFilter === "all" || invoice.status === statusFilter; return matchesSearch && matchesStatus; }); }, [searchQuery, statusFilter]); const sortedInvoices = React.useMemo(() => { if (!sortKey) return filteredInvoices; return [...filteredInvoices].sort((a, b) => { let cmp = 0; switch (sortKey) { case "customer": cmp = a.customer.localeCompare(b.customer); break; case "invoiceId": cmp = a.invoiceId.localeCompare(b.invoiceId); break; case "date": cmp = a.date.localeCompare(b.date); break; case "amount": cmp = a.amount - b.amount; break; } return sortDir === "desc" ? -cmp : cmp; }); }, [filteredInvoices, sortKey, sortDir]); const totalPages = Math.max(1, Math.ceil(sortedInvoices.length / pageSize)); const paginatedInvoices = React.useMemo(() => { const startIndex = (currentPage - 1) * pageSize; return sortedInvoices.slice(startIndex, startIndex + pageSize); }, [sortedInvoices, currentPage, pageSize]); const goToPage = (page: number) => { setCurrentPage(Math.max(1, Math.min(page, totalPages))); }; const sortableHeader = ( label: string, key: InvoiceSortKey, className?: string, ) => ( ); return (

فاکتورهای اخیر

{sortedInvoices.length.toLocaleString("fa-IR")}

فاکتورهای اخیر در جدول زیر نمایش داده می‌شوند.

فیلتر بر اساس وضعیت setStatusFilter("all")} > همه وضعیت‌ها {invoiceStatuses.map((status) => ( setStatusFilter(status)} > {invoiceStatusLabels[status]} ))} {hasActiveFilters && ( <> )}
{hasActiveFilters && (
فیلترها: {statusFilter !== "all" && ( )}
)}
{sortableHeader("مشتری", "customer", "min-w-[160px]")} {sortableHeader("شناسه فاکتور", "invoiceId", "min-w-[120px]")} {sortableHeader( "تاریخ", "date", "hidden min-w-[100px] sm:table-cell", )} {sortableHeader("مبلغ", "amount", "min-w-[90px]")} اولویت وضعیت {paginatedInvoices.length === 0 ? ( فاکتوری با این فیلترها یافت نشد. ) : ( paginatedInvoices.map((invoice) => (
{invoice.customerInitials} {invoice.customer}
{invoice.invoiceId} {invoiceDateFormatter.format(new Date(invoice.date))} {currencyFormatter.format(invoice.amount)} {invoicePriorityLabels[invoice.priority]} {invoiceStatusLabels[invoice.status]}
)) )}
ردیف در هر صفحه: {sortedInvoices.length === 0 ? "۰" : `${((currentPage - 1) * pageSize + 1).toLocaleString("fa-IR")}-${Math.min( currentPage * pageSize, sortedInvoices.length, ).toLocaleString("fa-IR")}`}{" "} از {sortedInvoices.length.toLocaleString("fa-IR")}
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { let pageNum: number; if (totalPages <= 5) { pageNum = i + 1; } else if (currentPage <= 3) { pageNum = i + 1; } else if (currentPage >= totalPages - 2) { pageNum = totalPages - 4 + i; } else { pageNum = currentPage - 2 + i; } return ( ); })}
); }; const DashboardContent = () => { return (
); }; const Dashboard8 = ({ className }: { className?: string }) => { return ( رفتن به محتوای اصلی ); }; export { Dashboard8 };