Files
tgex-frontend/components/analytics/analytics-chart.tsx
2025-12-01 07:43:47 +03:00

182 lines
5.0 KiB
TypeScript

"use client";
import React from "react";
import { CartesianGrid, Line, LineChart, XAxis, Bar, BarChart } from "recharts";
import { CardContent } from "@/components/ui/card";
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
ChartLegend,
ChartLegendContent,
} from "@/components/ui/chart";
import { formatCurrency, formatNumber } from "@/lib/utils/format";
import type { SpendingDataPoint, AnalyticsMetric } from "@/lib/types/api";
interface AnalyticsChartProps {
data: SpendingDataPoint[];
metrics: AnalyticsMetric[];
chartType: "line" | "bar";
showDataLabels?: boolean;
}
const METRIC_LABELS: Record<AnalyticsMetric, string> = {
cost: "Расходы",
subscriptions: "Подписки",
views: "Просмотры",
cpf: "CPF",
cpm: "CPM",
};
export const AnalyticsChart: React.FC<AnalyticsChartProps> = ({
data,
metrics,
chartType,
showDataLabels = false,
}) => {
// Создаем конфигурацию для chart
const chartConfig: ChartConfig = metrics.reduce((acc, metric, index) => {
acc[metric] = {
label: METRIC_LABELS[metric],
color: `var(--chart-${index + 1})`,
};
return acc;
}, {} as ChartConfig);
const formatValue = (value: number | null, metric: AnalyticsMetric) => {
if (value === null) return "—";
if (metric === "cost" || metric === "cpf" || metric === "cpm") {
return formatCurrency(value);
}
return formatNumber(value);
};
if (chartType === "bar") {
return (
<CardContent className="px-2 sm:p-6">
<ChartContainer
config={chartConfig}
className="aspect-auto h-[350px] w-full"
>
<BarChart
accessibilityLayer
data={data}
margin={{
left: 12,
right: 12,
top: showDataLabels ? 20 : 5,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="period"
tickLine={false}
axisLine={false}
tickMargin={8}
minTickGap={32}
/>
<ChartTooltip
content={
<ChartTooltipContent
labelFormatter={(value) => `Период: ${value}`}
formatter={(value, name) => [
formatValue(
value as number,
name as AnalyticsMetric
),
chartConfig[name as AnalyticsMetric]?.label || name,
]}
/>
}
/>
<ChartLegend content={<ChartLegendContent />} />
{metrics.map((metric) => (
<Bar
key={metric}
dataKey={metric}
fill={chartConfig[metric]?.color}
radius={4}
label={
showDataLabels
? {
position: "top",
formatter: (value: number) =>
formatValue(value, metric),
fontSize: 10,
}
: undefined
}
/>
))}
</BarChart>
</ChartContainer>
</CardContent>
);
}
return (
<CardContent className="px-2 sm:p-6">
<ChartContainer
config={chartConfig}
className="aspect-auto h-[350px] w-full"
>
<LineChart
accessibilityLayer
data={data}
margin={{
left: 12,
right: 12,
top: showDataLabels ? 20 : 5,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="period"
tickLine={false}
axisLine={false}
tickMargin={8}
minTickGap={32}
/>
<ChartTooltip
content={
<ChartTooltipContent
labelFormatter={(value) => `Период: ${value}`}
formatter={(value, name) => [
formatValue(
value as number,
name as AnalyticsMetric
),
chartConfig[name as AnalyticsMetric]?.label || name,
]}
/>
}
/>
<ChartLegend content={<ChartLegendContent />} />
{metrics.map((metric) => (
<Line
key={metric}
dataKey={metric}
type="monotone"
stroke={chartConfig[metric]?.color}
strokeWidth={2}
dot={showDataLabels}
label={
showDataLabels
? {
position: "top",
formatter: (value: number) =>
formatValue(value, metric),
fontSize: 10,
}
: undefined
}
/>
))}
</LineChart>
</ChartContainer>
</CardContent>
);
};