"use client"; import { BarChart3, Bell, Box, ChevronRight, ChevronsUpDown, ClipboardList, Globe, HelpCircle, LayoutDashboard, LogOut, MessageSquare, MoreHorizontal, Package, RotateCcw, Search, Settings, Truck, User, Users, } from "lucide-react"; import * as React from "react"; import type { TooltipProps } from "recharts"; import { Bar, BarChart, CartesianGrid, Line, LineChart, Tooltip, XAxis, YAxis, } from "recharts"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { type ChartConfig, ChartContainer } 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 { ScrollArea } from "@/components/ui/scroll-area"; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarTrigger, } from "@/components/ui/sidebar"; import { TooltipProvider } 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 TimePeriod = "6months" | "year"; type StatGridItem = { label: string; value: number; format: "currency" | "number" | "percent" | "days" | "compact"; change: number; invertColor: boolean; }; type FulfillmentItem = { order: string; shipped: Date; progress: number; segments: 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: 1, }); const numberFormatter = new Intl.NumberFormat("fa-IR"); const compactNumberFormatter = new Intl.NumberFormat("fa-IR", { notation: "compact", }); const percentFormatter = new Intl.NumberFormat("fa-IR", { style: "percent", maximumFractionDigits: 1, }); const oneDecimalFormatter = new Intl.NumberFormat("fa-IR", { maximumFractionDigits: 1, }); const weekdayFormatter = new Intl.DateTimeFormat("fa-IR", { weekday: "short", }); const monthDayFormatter = new Intl.DateTimeFormat("fa-IR", { month: "short", day: "numeric", }); const shippedDateFormatter = new Intl.DateTimeFormat("fa-IR", { month: "short", day: "numeric", year: "numeric", }); const monthLabel = (monthIndex: number) => new Intl.DateTimeFormat("fa-IR", { month: "short" }).format( new Date(2025, monthIndex, 1), ); const formatStatValue = (stat: StatGridItem) => { switch (stat.format) { case "currency": return currencyFormatter.format(stat.value); case "percent": return percentFormatter.format(stat.value); case "days": return `${oneDecimalFormatter.format(stat.value)} روز`; case "compact": return compactNumberFormatter.format(stat.value); default: return numberFormatter.format(stat.value); } }; 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 revenueChartConfig = { thisYear: { label: "امسال", color: palette.primary }, prevYear: { label: "سال قبل", theme: palette.secondary }, } satisfies ChartConfig; 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: Box, href: "#", children: [ { label: "همه محصولات", icon: Package, href: "#" }, { label: "دسته‌بندی‌ها", icon: Package, href: "#" }, { label: "موجودی", icon: Package, href: "#" }, ], }, { label: "مشتریان", icon: Users, href: "#" }, ], }, { title: "تحلیل فروش", defaultOpen: true, items: [ { label: "تحلیل فروش", icon: BarChart3, href: "#" }, { label: "گزارش‌ها", icon: Globe, href: "#" }, ], }, { title: "سایر", defaultOpen: false, items: [ { label: "ارسال", icon: Truck, href: "#" }, { label: "پیام‌ها", icon: MessageSquare, href: "#" }, { label: "مرجوعی‌ها", icon: RotateCcw, href: "#" }, ], }, ], footerGroup: { title: "تنظیمات", items: [{ label: "تنظیمات", icon: Settings, href: "#" }], }, user: { name: "علی رضایی", email: "john@acme.store", avatar: "/block-media/shadcnblocks/block/avatar-4.webp", }, }; const fullYearData = [ { month: monthLabel(0), thisYear: 42000, prevYear: 38000 }, { month: monthLabel(1), thisYear: 38000, prevYear: 45000 }, { month: monthLabel(2), thisYear: 52000, prevYear: 41000 }, { month: monthLabel(3), thisYear: 45000, prevYear: 48000 }, { month: monthLabel(4), thisYear: 58000, prevYear: 44000 }, { month: monthLabel(5), thisYear: 41000, prevYear: 52000 }, { month: monthLabel(6), thisYear: 55000, prevYear: 47000 }, { month: monthLabel(7), thisYear: 48000, prevYear: 53000 }, { month: monthLabel(8), thisYear: 62000, prevYear: 49000 }, { month: monthLabel(9), thisYear: 54000, prevYear: 58000 }, { month: monthLabel(10), thisYear: 67000, prevYear: 52000 }, { month: monthLabel(11), thisYear: 71000, prevYear: 61000 }, ]; const periodLabels: Record = { "6months": "۶ ماه اخیر", year: "۱۲ ماه", }; function getDataForPeriod(period: TimePeriod) { if (period === "6months") return fullYearData.slice(0, 6); return fullYearData; } const statsGridData: StatGridItem[] = [ { label: "درآمد کل", value: 1_800_000, format: "currency", change: 12.0, invertColor: false, }, { label: "میانگین تکمیل", value: 2.4, format: "days", change: -5.0, invertColor: false, }, { label: "سفارش‌ها", value: 9420, format: "number", change: 18.0, invertColor: false, }, { label: "نرخ تبدیل", value: 0.042, format: "percent", change: 1.2, invertColor: false, }, { label: "نرخ مرجوعی", value: 0.031, format: "percent", change: -11.0, invertColor: true, }, { label: "بازدید صفحات", value: 184_000, format: "compact", change: 22.0, invertColor: false, }, ]; const categoryWeekData = [ { day: weekdayFormatter.format(new Date(2025, 0, 6)), electronics: 180, clothing: 220, homeGarden: 280, sports: 350, }, { day: weekdayFormatter.format(new Date(2025, 0, 7)), electronics: 420, clothing: 550, homeGarden: 380, sports: 280, }, { day: weekdayFormatter.format(new Date(2025, 0, 8)), electronics: 280, clothing: 380, homeGarden: 520, sports: 450, }, { day: weekdayFormatter.format(new Date(2025, 0, 9)), electronics: 550, clothing: 280, homeGarden: 420, sports: 620, }, { day: weekdayFormatter.format(new Date(2025, 0, 10)), electronics: 323, clothing: 729, homeGarden: 506, sports: 490, }, { day: weekdayFormatter.format(new Date(2025, 0, 11)), electronics: 480, clothing: 420, homeGarden: 280, sports: 380, }, { day: weekdayFormatter.format(new Date(2025, 0, 12)), electronics: 220, clothing: 550, homeGarden: 450, sports: 320, }, ]; const categoryMonthData = [ { day: monthDayFormatter.format(new Date(2025, 0, 1)), electronics: 180, clothing: 220, homeGarden: 280, sports: 350, }, { day: monthDayFormatter.format(new Date(2025, 0, 5)), electronics: 420, clothing: 550, homeGarden: 380, sports: 280, }, { day: monthDayFormatter.format(new Date(2025, 0, 9)), electronics: 280, clothing: 380, homeGarden: 520, sports: 450, }, { day: monthDayFormatter.format(new Date(2025, 0, 13)), electronics: 550, clothing: 280, homeGarden: 420, sports: 620, }, { day: monthDayFormatter.format(new Date(2025, 0, 15)), electronics: 323, clothing: 729, homeGarden: 506, sports: 490, }, { day: monthDayFormatter.format(new Date(2025, 0, 17)), electronics: 480, clothing: 420, homeGarden: 280, sports: 380, }, { day: monthDayFormatter.format(new Date(2025, 0, 21)), electronics: 220, clothing: 550, homeGarden: 450, sports: 320, }, { day: monthDayFormatter.format(new Date(2025, 0, 25)), electronics: 380, clothing: 320, homeGarden: 580, sports: 480, }, { day: monthDayFormatter.format(new Date(2025, 0, 30)), electronics: 520, clothing: 450, homeGarden: 350, sports: 420, }, ]; const categoryChartConfig = { electronics: { label: "الکترونیک", color: palette.primary }, clothing: { label: "پوشاک", theme: palette.secondary, }, homeGarden: { label: "خانه و باغ", theme: palette.tertiary, }, sports: { label: "ورزشی", theme: palette.quaternary, }, } satisfies ChartConfig; const fulfillmentData: FulfillmentItem[] = [ { order: "ORD-4821", shipped: new Date(2025, 0, 27), progress: 92, segments: [ 0.9, 0.7, 1.0, 0.8, 0.6, 0.9, 1.0, 0.7, 0.5, 0.8, 0.9, 1.0, 0.6, 0.7, 0.8, 0.9, 1.0, 0.5, 0.7, 0.8, 0.9, 0.6, 1.0, 0.8, 0.7, 0.3, 0.2, 0.1, 0.1, 0.1, ], }, { order: "ORD-4819", shipped: new Date(2025, 0, 26), progress: 78, segments: [ 0.8, 0.6, 0.9, 0.7, 1.0, 0.5, 0.8, 0.9, 0.6, 0.7, 1.0, 0.8, 0.5, 0.9, 0.7, 0.6, 0.8, 1.0, 0.7, 0.5, 0.2, 0.1, 0.15, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, ], }, { order: "ORD-4815", shipped: new Date(2025, 0, 25), progress: 100, segments: [ 1.0, 0.9, 0.8, 1.0, 0.7, 0.9, 1.0, 0.8, 0.6, 0.9, 1.0, 0.7, 0.8, 0.9, 1.0, 0.6, 0.8, 0.9, 1.0, 0.7, 0.9, 0.8, 1.0, 0.6, 0.9, 0.7, 1.0, 0.8, 0.9, 1.0, ], }, { order: "ORD-4812", shipped: new Date(2025, 0, 24), progress: 65, segments: [ 0.9, 1.0, 0.7, 0.8, 0.6, 0.9, 0.5, 0.8, 1.0, 0.7, 0.9, 0.6, 0.8, 0.5, 0.7, 1.0, 0.6, 0.9, 0.8, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, ], }, { order: "ORD-4808", shipped: new Date(2025, 0, 23), progress: 43, segments: [ 0.8, 0.7, 1.0, 0.6, 0.9, 0.8, 0.5, 0.7, 1.0, 0.9, 0.6, 0.8, 0.7, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, ], }, { order: "ORD-4805", shipped: new Date(2025, 0, 22), progress: 100, segments: [ 0.9, 0.8, 1.0, 0.7, 0.9, 0.6, 1.0, 0.8, 0.7, 0.9, 1.0, 0.8, 0.6, 0.9, 0.7, 1.0, 0.8, 0.9, 0.7, 1.0, 0.8, 0.9, 0.6, 1.0, 0.7, 0.8, 0.9, 1.0, 0.8, 0.9, ], }, { order: "ORD-4801", shipped: new Date(2025, 0, 21), progress: 88, segments: [ 1.0, 0.8, 0.7, 0.9, 0.6, 1.0, 0.8, 0.5, 0.9, 0.7, 1.0, 0.8, 0.6, 0.9, 0.7, 0.8, 1.0, 0.6, 0.5, 0.3, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, ], }, { order: "ORD-4798", shipped: new Date(2025, 0, 20), progress: 55, segments: [ 0.7, 0.9, 1.0, 0.6, 0.8, 0.9, 0.7, 1.0, 0.5, 0.8, 0.6, 0.9, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, ], }, { order: "ORD-4794", shipped: new Date(2025, 0, 19), progress: 100, segments: [ 0.8, 1.0, 0.9, 0.7, 0.8, 1.0, 0.6, 0.9, 0.8, 1.0, 0.7, 0.9, 0.8, 1.0, 0.6, 0.9, 0.7, 1.0, 0.8, 0.9, 0.7, 1.0, 0.8, 0.6, 0.9, 1.0, 0.7, 0.8, 0.9, 1.0, ], }, { order: "ORD-4790", shipped: new Date(2025, 0, 18), progress: 71, segments: [ 0.9, 0.6, 0.8, 1.0, 0.7, 0.9, 0.5, 0.8, 1.0, 0.6, 0.9, 0.7, 0.8, 0.5, 0.3, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, ], }, { order: "ORD-4786", shipped: new Date(2025, 0, 17), progress: 35, segments: [ 1.0, 0.8, 0.9, 0.7, 0.6, 0.8, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, ], }, { order: "ORD-4782", shipped: new Date(2025, 0, 16), progress: 96, segments: [ 0.8, 0.9, 1.0, 0.7, 0.8, 0.9, 1.0, 0.6, 0.8, 0.9, 1.0, 0.7, 0.9, 0.8, 1.0, 0.6, 0.9, 0.7, 1.0, 0.8, 0.9, 0.6, 1.0, 0.8, 0.7, 0.9, 1.0, 0.8, 0.3, 0.1, ], }, ]; 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 (
); }; function CustomTooltip({ active, payload, label, colors, }: any & { colors: { primary: string; secondary: string }; }) { if (!active || !payload?.length) return null; const thisYear = payload.find((p: any) => p.dataKey === "thisYear")?.value || 0; const prevYear = payload.find((p: any) => p.dataKey === "prevYear")?.value || 0; const diff = Number(thisYear) - Number(prevYear); const percentage = prevYear ? Math.round((diff / Number(prevYear)) * 100) : 0; const currentYear = new Date().getFullYear(); return (

{label}، {currentYear.toLocaleString("fa-IR")}

امسال: {currencyFormatter.format(Number(thisYear))}
سال قبل: {currencyFormatter.format(Number(prevYear))}
= 0 ? "text-emerald-500" : "text-red-500", )} > {diff >= 0 ? "+" : ""} {percentage.toLocaleString("fa-IR")}٪ نسبت به سال قبل
); } const RevenueFlowChart = () => { const [period, setPeriod] = React.useState("6months"); React.useEffect(() => { if (typeof window === "undefined") return; const params = new URLSearchParams(window.location.search); const nextPeriod = params.get("period"); if (nextPeriod === "6months" || nextPeriod === "year") { setPeriod(nextPeriod); } }, []); React.useEffect(() => { if (typeof window === "undefined") return; const params = new URLSearchParams(window.location.search); if (period !== "6months") { params.set("period", period); } else { params.delete("period"); } const nextQuery = params.toString(); const nextUrl = nextQuery ? `${window.location.pathname}?${nextQuery}` : window.location.pathname; window.history.replaceState(null, "", nextUrl); }, [period]); const chartData = getDataForPeriod(period); const totalRevenue = chartData.reduce((acc, item) => acc + item.thisYear, 0); return (

{currencyFormatter.format(totalRevenue)}

درآمد کل ({periodLabels[period]})

امسال
سال قبل
بازه زمانی {(Object.keys(periodLabels) as TimePeriod[]).map((key) => ( setPeriod(key)} > {periodLabels[key]} ))}
compactCurrencyFormatter.format(value)} width={40} /> } cursor={{ fillOpacity: 0.05, radius: 4 }} />
); }; const StatsCardsGrid = () => { return (
{statsGridData.map((stat) => { const isPositive = stat.invertColor ? stat.change < 0 : stat.change > 0; return (
{stat.label}
{formatStatValue(stat)}
{stat.change > 0 ? "+" : ""} {stat.change.toLocaleString("fa-IR", { minimumFractionDigits: 1, maximumFractionDigits: 1, })} ٪ نسبت به ماه قبل
); })}
); }; const CategoryPerformanceChart = () => { const [tab, setTab] = React.useState<"week" | "month">("week"); React.useEffect(() => { if (typeof window === "undefined") return; const params = new URLSearchParams(window.location.search); const nextTab = params.get("categoryTab"); if (nextTab === "week" || nextTab === "month") { setTab(nextTab); } }, []); React.useEffect(() => { if (typeof window === "undefined") return; const params = new URLSearchParams(window.location.search); if (tab !== "week") { params.set("categoryTab", tab); } else { params.delete("categoryTab"); } const nextQuery = params.toString(); const nextUrl = nextQuery ? `${window.location.pathname}?${nextQuery}` : window.location.pathname; window.history.replaceState(null, "", nextUrl); }, [tab]); const chartData = tab === "week" ? categoryWeekData : categoryMonthData; const totalOrders = chartData.reduce( (acc, item) => acc + item.electronics + item.clothing + item.homeGarden + item.sports, 0, ); return (

عملکرد دسته‌ها

{numberFormatter.format(totalOrders)}
{Object.entries(categoryChartConfig).map(([key, config]) => (
{config.label}
))}
{( [ { value: "week" as const, label: "هفته" }, { value: "month" as const, label: "ماه" }, ] as const ).map(({ value, label }) => ( ))}
{ if (!active || !payload?.length) return null; return (

{label}

{payload.map((entry) => (
{categoryChartConfig[ entry.dataKey as keyof typeof categoryChartConfig ]?.label ?? entry.dataKey} : {numberFormatter.format(Number(entry.value))}
))}
); }} />
); }; const FulfillmentPanel = () => { return (

تکمیل سفارش

خروجی جدول مشاهده همه سفارش‌ها
سفارش ارسال وضعیت تحویل٪
{fulfillmentData.map((row) => (
{row.order} {shippedDateFormatter.format(row.shipped)}
{row.segments.slice(0, 15).map((opacity, i) => { const filled = i < Math.round((row.progress / 100) * 15); return (
); })}
{row.progress.toLocaleString("fa-IR")}
))}
); }; const DashboardContent = () => { return (
); }; const Dashboard6 = ({ className }: { className?: string }) => { return ( رفتن به محتوای اصلی ); }; export { Dashboard6 };