"use client"; import { addDays, differenceInDays, endOfMonth, endOfWeek, format as formatDate, startOfDay, startOfMonth, startOfWeek, subDays, subMonths, } from "date-fns"; import { faIR } from "date-fns/locale"; import { Activity, CalendarIcon, ChevronLeft, Globe, TrendingDown, TrendingUp, Users, Zap, } from "lucide-react"; import { useMemo, useState } from "react"; import type { DateRange } from "react-day-picker"; import { Area, AreaChart, CartesianGrid, Pie, PieChart, XAxis, YAxis, } from "recharts"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Calendar } from "@/components/ui/calendar"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { cn } from "@/lib/utils"; interface ChartGroup14Props { className?: string; } const seededRandom = (seed: number) => { const x = Math.sin(seed) * 10000; return x - Math.floor(x); }; const currencyFormatter = new Intl.NumberFormat("fa-IR", { style: "currency", currency: "IRR", minimumFractionDigits: 0, maximumFractionDigits: 0, }); const compactCurrencyFormatter = new Intl.NumberFormat("fa-IR", { style: "currency", currency: "IRR", notation: "compact", minimumFractionDigits: 0, maximumFractionDigits: 1, }); const numberFormatter = new Intl.NumberFormat("fa-IR", { maximumFractionDigits: 0, }); const oneDecimalFormatter = new Intl.NumberFormat("fa-IR", { maximumFractionDigits: 1, minimumFractionDigits: 1, }); const percentChangeFormatter = new Intl.NumberFormat("fa-IR", { maximumFractionDigits: 1, minimumFractionDigits: 1, signDisplay: "exceptZero", }); const formatPercent = (value: number) => `${oneDecimalFormatter.format(value)}٪`; const generateDataForRange = (from: Date, to: Date) => { const data = []; const days = differenceInDays(to, from) + 1; for (let i = 0; i < days; i++) { const date = addDays(startOfDay(from), i); const seed = date.getTime() / 86400000; data.push({ date: date, dateStr: formatDate(date, "d MMM", { locale: faIR }), revenue: Math.floor(seededRandom(seed) * 15000) + 8000, sessions: Math.floor(seededRandom(seed + 1000) * 3000) + 1500, conversions: Math.floor(seededRandom(seed + 2000) * 150) + 50, bounceRate: Math.floor(seededRandom(seed + 3000) * 30) + 20, }); } return data; }; const channelData = [ { name: "جستجوی ارگانیک", value: 38, fill: "var(--chart-1)" }, { name: "مستقیم", value: 26, fill: "var(--chart-2)" }, { name: "ارجاع", value: 18, fill: "var(--chart-3)" }, { name: "شبکه‌های اجتماعی", value: 12, fill: "var(--chart-4)" }, { name: "ایمیل", value: 6, fill: "var(--chart-5)" }, ]; const channelConfig = { "جستجوی ارگانیک": { label: "جستجوی ارگانیک", color: "var(--chart-1)", }, مستقیم: { label: "مستقیم", color: "var(--chart-2)" }, ارجاع: { label: "ارجاع", color: "var(--chart-3)" }, "شبکه‌های اجتماعی": { label: "شبکه‌های اجتماعی", color: "var(--chart-4)", }, ایمیل: { label: "ایمیل", color: "var(--chart-5)" }, } satisfies ChartConfig; const topPages = [ { path: "/products", views: 12453, change: 12.5 }, { path: "/pricing", views: 8234, change: 8.2 }, { path: "/about", views: 6123, change: -2.1 }, { path: "/blog/guide", views: 4532, change: 15.8 }, { path: "/contact", views: 3421, change: 5.4 }, ]; const topUsers = [ { name: "سارا احمدی", email: "sara@example.com", sessions: 47, avatar: "/block-media/shadcnblocks/ui/avatar-1.jpg", }, { name: "علی رضایی", email: "ali@example.com", sessions: 42, avatar: "/block-media/shadcnblocks/ui/avatar-2.jpg", }, { name: "مریم حسینی", email: "maryam@example.com", sessions: 38, avatar: "/block-media/shadcnblocks/ui/avatar-3.jpg", }, { name: "محمد کریمی", email: "mohammad@example.com", sessions: 35, avatar: "/block-media/shadcnblocks/ui/avatar-4.jpg", }, ]; const presetRanges = [ { label: "امروز", getValue: () => ({ from: new Date(), to: new Date() }) }, { label: "این هفته", getValue: () => ({ from: startOfWeek(new Date(), { locale: faIR }), to: endOfWeek(new Date(), { locale: faIR }), }), }, { label: "این ماه", getValue: () => ({ from: startOfMonth(new Date()), to: endOfMonth(new Date()), }), }, { label: "۷ روز اخیر", getValue: () => ({ from: subDays(new Date(), 6), to: new Date() }), }, { label: "۳۰ روز اخیر", getValue: () => ({ from: subDays(new Date(), 29), to: new Date() }), }, { label: "۳ ماه اخیر", getValue: () => ({ from: subMonths(new Date(), 3), to: new Date() }), }, ]; const revenueConfig = { revenue: { label: "درآمد", color: "var(--chart-1)" }, } satisfies ChartConfig; const ChartGroup14 = ({ className }: ChartGroup14Props) => { const [dateRange, setDateRange] = useState({ from: subDays(new Date(), 29), to: new Date(), }); const [selectedPreset, setSelectedPreset] = useState("۳۰ روز اخیر"); const filteredData = useMemo(() => { const from = dateRange?.from ?? subDays(new Date(), 29); const to = dateRange?.to ?? new Date(); return generateDataForRange(from, to); }, [dateRange]); const stats = useMemo(() => { const totalRevenue = filteredData.reduce( (sum, item) => sum + item.revenue, 0, ); const totalSessions = filteredData.reduce( (sum, item) => sum + item.sessions, 0, ); const totalConversions = filteredData.reduce( (sum, item) => sum + item.conversions, 0, ); const avgBounceRate = filteredData.length > 0 ? Math.round( filteredData.reduce((sum, item) => sum + item.bounceRate, 0) / filteredData.length, ) : 0; const conversionRate = totalSessions > 0 ? (totalConversions / totalSessions) * 100 : 0; return { totalRevenue, totalSessions, totalConversions, avgBounceRate, conversionRate, }; }, [filteredData]); const handlePresetChange = (value: string) => { const preset = presetRanges.find((p) => p.label === value); if (preset) { setDateRange(preset.getValue()); setSelectedPreset(value); } }; return (

داشبورد تحلیل‌ها

بینش لحظه‌ای از کسب‌وکار شما

{ setDateRange(range); setSelectedPreset("بازه دلخواه"); }} />
{percentChangeFormatter.format(12.5)}٪

مجموع درآمد

{currencyFormatter.format(stats.totalRevenue)}

{percentChangeFormatter.format(8.2)}٪

مجموع جلسات

{numberFormatter.format(stats.totalSessions)}

{percentChangeFormatter.format(5.4)}٪

نرخ تبدیل

{formatPercent(stats.conversionRate)}

{percentChangeFormatter.format(2.1)}٪

نرخ پرش

{formatPercent(stats.avgBounceRate)}

روند درآمد compactCurrencyFormatter.format(Number(v)) } /> currencyFormatter.format(Number(value)) } /> } cursor={{ stroke: "var(--border)", strokeDasharray: "4 4" }} /> کانال‌های ترافیک
formatPercent(Number(value))} /> } cursor={false} />
{numberFormatter.format(stats.totalSessions)}
{channelData.map((item, index) => (
{item.name}
{formatPercent(item.value)}
))}
پرطرفدارترین صفحات
{topPages.map((page, index) => (
{numberFormatter.format(index + 1)} {page.path}
{numberFormatter.format(page.views)} = 0 ? "text-green-600" : "text-red-600", )} > {percentChangeFormatter.format(page.change)}٪
))}
فعال‌ترین کاربران
{topUsers.map((user, index) => (
{user.name .split(" ") .map((n) => n[0]) .join("")}

{user.name}

{user.email}

{numberFormatter.format(user.sessions)} جلسه
))}
); }; export { ChartGroup14 };