feat: add FunnelChart, RevenueChart, ObjectTypeChart, AvgTimeChart

This commit is contained in:
2026-05-07 20:29:06 +03:00
parent ff84f89f29
commit b19ab501cb
4 changed files with 114 additions and 0 deletions
@@ -0,0 +1,30 @@
import React from 'react';
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Cell } from 'recharts';
const COLORS = ['#f59e0b', '#0ea5e9', '#8b5cf6', '#f97316'];
const AvgTimeChart = ({ data }) => (
<div>
<h3 style={{ fontFamily: 'var(--font-heading)', fontSize: '0.95rem', fontWeight: 700, marginBottom: '14px' }}>
Среднее время в стадии (дни)
</h3>
{data.every((d) => d.avgDays === 0) ? (
<p style={{ color: 'var(--text-muted)', fontSize: '0.85rem', textAlign: 'center', padding: '40px 0' }}>
Недостаточно данных
</p>
) : (
<ResponsiveContainer width="100%" height={210}>
<BarChart data={data} margin={{ left: 10, right: 20 }}>
<XAxis dataKey="status" tick={{ fontSize: 11, fill: '#94a3b8' }} />
<YAxis tick={{ fontSize: 11, fill: '#94a3b8' }} unit=" д" />
<Tooltip formatter={(v) => [`${v} дней`]} />
<Bar dataKey="avgDays" radius={[4, 4, 0, 0]}>
{data.map((entry, i) => <Cell key={entry.status} fill={COLORS[i % COLORS.length]} />)}
</Bar>
</BarChart>
</ResponsiveContainer>
)}
</div>
);
export default AvgTimeChart;
@@ -0,0 +1,28 @@
import React from 'react';
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Cell } from 'recharts';
const COLORS = ['#f59e0b', '#0ea5e9', '#8b5cf6', '#f97316', '#22c55e'];
const FunnelChart = ({ data }) => (
<div>
<h3 style={{ fontFamily: 'var(--font-heading)', fontSize: '0.95rem', fontWeight: 700, marginBottom: '14px' }}>
Воронка по стадиям
</h3>
{data.every((d) => d.count === 0) ? (
<p style={{ color: 'var(--text-muted)', fontSize: '0.85rem', textAlign: 'center', padding: '40px 0' }}>Нет данных</p>
) : (
<ResponsiveContainer width="100%" height={210}>
<BarChart data={data} layout="vertical" margin={{ left: 10, right: 30 }}>
<XAxis type="number" tick={{ fontSize: 11, fill: '#94a3b8' }} />
<YAxis type="category" dataKey="status" tick={{ fontSize: 11, fill: '#94a3b8' }} width={95} />
<Tooltip formatter={(v) => [`${v} заказов`]} />
<Bar dataKey="count" radius={[0, 4, 4, 0]}>
{data.map((entry, i) => <Cell key={entry.status} fill={COLORS[i]} />)}
</Bar>
</BarChart>
</ResponsiveContainer>
)}
</div>
);
export default FunnelChart;
@@ -0,0 +1,31 @@
import React from 'react';
import { PieChart, Pie, Cell, Tooltip, Legend, ResponsiveContainer } from 'recharts';
const COLORS = ['#0ea5e9', '#f59e0b', '#8b5cf6', '#22c55e', '#f97316'];
const TYPE_LABELS = {
industrial: 'Промышленный', commercial: 'Коммерческий',
infrastructure: 'Инфраструктура', residential: 'Частный', other: 'Другое',
};
const ObjectTypeChart = ({ data }) => (
<div>
<h3 style={{ fontFamily: 'var(--font-heading)', fontSize: '0.95rem', fontWeight: 700, marginBottom: '14px' }}>
Типы объектов
</h3>
{data.length === 0 ? (
<p style={{ color: 'var(--text-muted)', fontSize: '0.85rem', textAlign: 'center', padding: '40px 0' }}>Нет данных</p>
) : (
<ResponsiveContainer width="100%" height={210}>
<PieChart>
<Pie data={data} dataKey="count" nameKey="type" cx="50%" cy="50%" innerRadius={50} outerRadius={80}>
{data.map((entry, i) => <Cell key={entry.type} fill={COLORS[i % COLORS.length]} />)}
</Pie>
<Tooltip formatter={(v, name) => [v, TYPE_LABELS[name] || name]} />
<Legend formatter={(v) => TYPE_LABELS[v] || v} iconSize={10} />
</PieChart>
</ResponsiveContainer>
)}
</div>
);
export default ObjectTypeChart;
@@ -0,0 +1,25 @@
import React from 'react';
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } from 'recharts';
const RevenueChart = ({ data }) => (
<div>
<h3 style={{ fontFamily: 'var(--font-heading)', fontSize: '0.95rem', fontWeight: 700, marginBottom: '14px' }}>
Выручка по месяцам ()
</h3>
{data.length === 0 ? (
<p style={{ color: 'var(--text-muted)', fontSize: '0.85rem', textAlign: 'center', padding: '40px 0' }}>Нет закрытых заказов</p>
) : (
<ResponsiveContainer width="100%" height={210}>
<LineChart data={data} margin={{ left: 10, right: 20 }}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(15,23,42,0.05)" />
<XAxis dataKey="month" tick={{ fontSize: 11, fill: '#94a3b8' }} />
<YAxis tick={{ fontSize: 11, fill: '#94a3b8' }} tickFormatter={(v) => `${(v / 1000).toFixed(0)}к`} />
<Tooltip formatter={(v) => [`${v.toLocaleString('ru-RU')}`, 'Выручка']} />
<Line type="monotone" dataKey="revenue" stroke="var(--accent-blue)" strokeWidth={2} dot={{ fill: 'var(--accent-blue)', r: 4 }} />
</LineChart>
</ResponsiveContainer>
)}
</div>
);
export default RevenueChart;