fix(charts): единый стиль AreaChart с градиентом
CI / test (push) Successful in 1m8s
CI / push-image (push) Successful in 6m9s
CI / ci-done (push) Successful in 0s
Deploy stage (admin) / deploy-stage-admin (push) Successful in 4m46s

Общий AreaTrendChart для мониторинга и daily-графиков, StatisticSparklineCard на дашборде.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-13 17:55:24 +03:00
parent d056b63e1b
commit de379e28bf
4 changed files with 143 additions and 42 deletions
+68
View File
@@ -0,0 +1,68 @@
import React from 'react';
import {
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from 'recharts';
export interface AreaTrendSeries {
key: string;
color: string;
name: string;
}
interface Props {
data: Record<string, unknown>[];
xKey: string;
series: AreaTrendSeries[];
height?: number;
xMinTickGap?: number;
yAllowDecimals?: boolean;
}
const AreaTrendChart: React.FC<Props> = ({
data,
xKey,
series,
height = 280,
xMinTickGap = 30,
yAllowDecimals = true,
}) => (
<ResponsiveContainer width="100%" height={height}>
<AreaChart data={data}>
<defs>
{series.map((s) => (
<linearGradient key={s.key} id={`grad-${s.key}`} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={s.color} stopOpacity={0.4} />
<stop offset="95%" stopColor={s.color} stopOpacity={0.05} />
</linearGradient>
))}
</defs>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey={xKey} minTickGap={xMinTickGap} />
<YAxis allowDecimals={yAllowDecimals} />
<Tooltip />
<Legend />
{series.map((s) => (
<Area
key={s.key}
type="monotone"
dataKey={s.key}
stroke={s.color}
fill={`url(#grad-${s.key})`}
strokeWidth={2}
dot={false}
activeDot={{ r: 3, strokeWidth: 0 }}
name={s.name}
/>
))}
</AreaChart>
</ResponsiveContainer>
);
export default AreaTrendChart;
+8 -20
View File
@@ -1,16 +1,7 @@
import React from 'react';
import { Card, Col } from 'antd';
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from 'recharts';
import { DayCount } from '../types/api';
import AreaTrendChart from './AreaTrendChart';
interface Props {
title: string;
@@ -32,16 +23,13 @@ const DailyLineChartCard: React.FC<Props> = ({ title, data, color, lg = 12 }) =>
return (
<Col xs={24} lg={lg}>
<Card title={title}>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis allowDecimals={false} />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="count" stroke={color} name={title} />
</LineChart>
</ResponsiveContainer>
<AreaTrendChart
data={chartData}
xKey="date"
series={[{ key: 'count', color, name: title }]}
height={300}
yAllowDecimals={false}
/>
</Card>
</Col>
);
+65
View File
@@ -0,0 +1,65 @@
import React from 'react';
import { Card, Statistic } from 'antd';
import { AreaChart, Area, XAxis, Tooltip } from 'recharts';
interface SparklinePoint {
date: string;
value: number;
}
interface Props {
title: string;
value: number;
prefix?: React.ReactNode;
data?: SparklinePoint[];
color?: string;
gradientId: string;
}
const StatisticSparklineCard: React.FC<Props> = ({
title,
value,
prefix,
data,
color = '#1890ff',
gradientId,
}) => (
<Card variant="outlined">
<div style={{ display: 'flex', alignItems: 'center' }}>
<Statistic
title={title}
value={value}
prefix={prefix}
style={{ flex: '0 0 auto', marginRight: 16 }}
/>
{data && data.length > 0 && (
<AreaChart
width={150}
height={50}
data={data}
margin={{ top: 5, right: 0, left: 0, bottom: 0 }}
>
<defs>
<linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={color} stopOpacity={0.4} />
<stop offset="95%" stopColor={color} stopOpacity={0.05} />
</linearGradient>
</defs>
<XAxis dataKey="date" hide />
<Tooltip labelFormatter={(label) => `Дата: ${label}`} />
<Area
type="monotone"
dataKey="value"
stroke={color}
fill={`url(#${gradientId})`}
strokeWidth={2}
dot={false}
activeDot={{ r: 3, strokeWidth: 0 }}
/>
</AreaChart>
)}
</div>
</Card>
);
export default StatisticSparklineCard;
+2 -22
View File
@@ -1,18 +1,9 @@
import React, { useEffect, useMemo, useState } from 'react';
import { Card, Col, Row, Select, Table, Tabs } from 'antd';
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from 'recharts';
import dayjs from 'dayjs';
import { useMetricsStore, NodeMetric } from '../../store/metricsStore';
import { monitoringApi } from '../../api/monitoringApi';
import AreaTrendChart from '../../components/AreaTrendChart';
const NODE_COLORS = ['#1677ff', '#52c41a', '#fa8c16', '#eb2f96', '#722ed1', '#13c2c2'];
const TIME_RANGES = [
@@ -51,18 +42,7 @@ const MetricChart: React.FC<{
data: ReturnType<typeof buildChartData>;
lines: Array<{ key: string; color: string; name: string }>;
}> = ({ data, lines }) => (
<ResponsiveContainer width="100%" height={280}>
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="time" minTickGap={30} />
<YAxis />
<Tooltip />
<Legend />
{lines.map((line) => (
<Line key={line.key} type="monotone" dataKey={line.key} stroke={line.color} name={line.name} dot={false} />
))}
</LineChart>
</ResponsiveContainer>
<AreaTrendChart data={data} xKey="time" series={lines} />
);
const MonitoringPage: React.FC = () => {