Files
reui-blocks/blocks/chart-group8.tsx
T

328 lines
10 KiB
TypeScript

"use client";
import { TrendingDown, TrendingUp } from "lucide-react";
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { cn } from "@/lib/utils";
interface ChartGroup8Props {
className?: string;
}
const weekdayFormatter = new Intl.DateTimeFormat("fa-IR", {
weekday: "short",
});
const currencyFormatter = new Intl.NumberFormat("fa-IR", {
style: "currency",
currency: "IRR",
minimumFractionDigits: 0,
maximumFractionDigits: 0,
});
const numberFormatter = new Intl.NumberFormat("fa-IR", {
maximumFractionDigits: 0,
});
const percentFormatter = new Intl.NumberFormat("fa-IR", {
maximumFractionDigits: 0,
});
const percentChangeFormatter = new Intl.NumberFormat("fa-IR", {
maximumFractionDigits: 1,
minimumFractionDigits: 1,
signDisplay: "exceptZero",
});
const formatPercent = (value: number) =>
`${percentFormatter.format(value)}٪`;
const weeklyData = [
{
day: weekdayFormatter.format(new Date(2025, 0, 6)),
current: 420,
previous: 380,
},
{
day: weekdayFormatter.format(new Date(2025, 0, 7)),
current: 510,
previous: 450,
},
{
day: weekdayFormatter.format(new Date(2025, 0, 8)),
current: 480,
previous: 420,
},
{
day: weekdayFormatter.format(new Date(2025, 0, 9)),
current: 620,
previous: 550,
},
{
day: weekdayFormatter.format(new Date(2025, 0, 10)),
current: 710,
previous: 620,
},
{
day: weekdayFormatter.format(new Date(2025, 0, 11)),
current: 540,
previous: 480,
},
{
day: weekdayFormatter.format(new Date(2025, 0, 12)),
current: 410,
previous: 390,
},
];
const performanceData = [
{ metric: "فروش", value: 87, target: 100, fill: "var(--chart-1)" },
{ metric: "سرنخ‌ها", value: 65, target: 100, fill: "var(--chart-2)" },
{ metric: "تبدیل", value: 42, target: 100, fill: "var(--chart-3)" },
{ metric: "حفظ مشتری", value: 91, target: 100, fill: "var(--chart-4)" },
];
const weeklyConfig = {
current: { label: "این هفته", color: "var(--chart-1)" },
previous: { label: "هفته قبل", color: "var(--chart-4)" },
} satisfies ChartConfig;
const monthlyGoalPercent = 73;
const monthlyGoalCurrent = 36500;
const monthlyGoalTarget = 50000;
const quickStats = [
{
label: "میانگین سفارش",
value: currencyFormatter.format(142),
change: 8.5,
},
{
label: "مشتریان جدید",
value: numberFormatter.format(284),
change: 12.3,
},
{
label: "نرخ تکرار",
value: formatPercent(34),
change: -2.1,
},
];
const ChartGroup8 = ({ className }: ChartGroup8Props) => {
const currentTotal = weeklyData.reduce((sum, d) => sum + d.current, 0);
const previousTotal = weeklyData.reduce((sum, d) => sum + d.previous, 0);
const changePercent =
((currentTotal - previousTotal) / previousTotal) * 100;
const isPositive = changePercent >= 0;
return (
<section
dir="rtl"
lang="fa"
className={cn("py-32 text-start", className)}
>
<div className="container grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card className="text-start lg:col-span-2">
<CardHeader className="pb-2">
<div className="flex items-center justify-between gap-4">
<div>
<CardTitle className="text-base">مقایسه هفتگی</CardTitle>
<CardDescription>این هفته در برابر هفته قبل</CardDescription>
</div>
<div>
<p className="text-xl font-bold tabular-nums">
{numberFormatter.format(currentTotal)}
</p>
<div className="flex items-center gap-1 text-sm tabular-nums">
{isPositive ? (
<TrendingUp
className="size-4 text-green-500"
aria-hidden="true"
/>
) : (
<TrendingDown
className="size-4 text-red-500"
aria-hidden="true"
/>
)}
<span
className={isPositive ? "text-green-500" : "text-red-500"}
>
{percentChangeFormatter.format(changePercent)}٪
</span>
</div>
</div>
</div>
</CardHeader>
<CardContent>
<ChartContainer config={weeklyConfig} className="h-52 w-full">
<LineChart
data={weeklyData}
margin={{ top: 10, right: 10, left: 0, bottom: 0 }}
>
<CartesianGrid strokeDasharray="3 3" vertical={false} />
<XAxis
dataKey="day"
axisLine={false}
tickLine={false}
tickMargin={8}
fontSize={12}
/>
<YAxis
axisLine={false}
tickLine={false}
tickMargin={8}
fontSize={10}
width={40}
tickFormatter={(v) => numberFormatter.format(Number(v))}
/>
<ChartTooltip
content={
<ChartTooltipContent
formatter={(value) =>
numberFormatter.format(Number(value))
}
/>
}
cursor={{ stroke: "var(--border)", strokeDasharray: "4 4" }}
/>
<Line
type="monotone"
dataKey="current"
stroke="var(--color-current)"
strokeWidth={2}
dot={{ fill: "var(--color-current)", r: 4 }}
/>
<Line
type="monotone"
dataKey="previous"
stroke="var(--color-previous)"
strokeWidth={2}
strokeDasharray="4 4"
dot={{ fill: "var(--color-previous)", r: 3 }}
/>
</LineChart>
</ChartContainer>
</CardContent>
</Card>
<Card className="text-start">
<CardHeader className="pb-2">
<CardTitle className="text-base">هدف ماهانه</CardTitle>
<CardDescription>پیشرفت هدف درآمد</CardDescription>
</CardHeader>
<CardContent className="flex flex-col items-center justify-center">
<div className="relative flex size-36 items-center justify-center">
<svg className="size-full -rotate-90" viewBox="0 0 100 100">
<circle
cx="50"
cy="50"
r="40"
fill="none"
className="stroke-muted"
strokeWidth="8"
/>
<circle
cx="50"
cy="50"
r="40"
fill="none"
className="stroke-chart-1"
strokeWidth="8"
strokeLinecap="round"
strokeDasharray={`${monthlyGoalPercent * 2.51} ${100 * 2.51}`}
/>
</svg>
<div className="absolute flex flex-col items-center">
<span className="text-xl font-bold tabular-nums">
{formatPercent(monthlyGoalPercent)}
</span>
<span className="text-xs text-muted-foreground">از هدف</span>
</div>
</div>
<p className="mt-2 text-center text-sm text-muted-foreground tabular-nums">
{currencyFormatter.format(monthlyGoalCurrent)} /{" "}
{currencyFormatter.format(monthlyGoalTarget)}
</p>
</CardContent>
</Card>
<Card className="text-start">
<CardHeader className="pb-2">
<CardTitle className="text-base">آمار سریع</CardTitle>
<CardDescription>نمای کلی شاخص‌های کلیدی</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{quickStats.map((stat, index) => (
<div key={index} className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">
{stat.label}
</span>
<div className="flex items-center gap-2 tabular-nums">
<span className="font-semibold">{stat.value}</span>
<span
className={cn(
"text-xs",
stat.change >= 0 ? "text-green-500" : "text-red-500",
)}
>
{percentChangeFormatter.format(stat.change)}٪
</span>
</div>
</div>
))}
</CardContent>
</Card>
<Card className="text-start lg:col-span-4">
<CardHeader className="pb-2">
<CardTitle className="text-base">شاخص‌های عملکرد</CardTitle>
<CardDescription>
پیشرفت نسبت به اهداف سه‌ماهه
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
{performanceData.map((item, index) => (
<div key={index} className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">{item.metric}</span>
<span className="text-sm text-muted-foreground tabular-nums">
{formatPercent(item.value)}
</span>
</div>
<div className="h-2 overflow-hidden rounded-full bg-muted">
<div
className="h-full rounded-full transition-all"
style={{
width: `${item.value}%`,
backgroundColor: item.fill,
}}
/>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
</section>
);
};
export { ChartGroup8 };