feat: creats frontend for the project
This commit is contained in:
101
frontend/src/components/CategoryChart.tsx
Normal file
101
frontend/src/components/CategoryChart.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import {
|
||||
PieChart,
|
||||
Pie,
|
||||
Cell,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
} from 'recharts';
|
||||
import type { ByCategoryItem } from '@family-budget/shared';
|
||||
import { formatAmount } from '../utils/format';
|
||||
|
||||
interface Props {
|
||||
data: ByCategoryItem[];
|
||||
}
|
||||
|
||||
const COLORS = [
|
||||
'#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6',
|
||||
'#ec4899', '#06b6d4', '#84cc16', '#f97316', '#6366f1',
|
||||
'#14b8a6', '#e11d48', '#0ea5e9', '#a855f7', '#22c55e',
|
||||
];
|
||||
|
||||
const rubFormatter = new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: 'RUB',
|
||||
maximumFractionDigits: 0,
|
||||
});
|
||||
|
||||
export function CategoryChart({ data }: Props) {
|
||||
if (data.length === 0) {
|
||||
return <div className="chart-empty">Нет данных за период</div>;
|
||||
}
|
||||
|
||||
const chartData = data.map((item) => ({
|
||||
name: item.categoryName,
|
||||
value: Math.abs(item.amount) / 100,
|
||||
txCount: item.txCount,
|
||||
share: item.share,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="category-chart-wrapper">
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={chartData}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
outerRadius={100}
|
||||
dataKey="value"
|
||||
nameKey="name"
|
||||
label={({ name, share }: { name: string; share: number }) =>
|
||||
`${name} ${(share * 100).toFixed(0)}%`
|
||||
}
|
||||
labelLine
|
||||
>
|
||||
{chartData.map((_, idx) => (
|
||||
<Cell
|
||||
key={idx}
|
||||
fill={COLORS[idx % COLORS.length]}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip
|
||||
formatter={(value: number) => rubFormatter.format(value)}
|
||||
/>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
|
||||
<table className="category-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Категория</th>
|
||||
<th>Сумма</th>
|
||||
<th className="th-center">Операций</th>
|
||||
<th className="th-center">Доля</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((item, idx) => (
|
||||
<tr key={item.categoryId}>
|
||||
<td>
|
||||
<span
|
||||
className="color-dot"
|
||||
style={{
|
||||
backgroundColor:
|
||||
COLORS[idx % COLORS.length],
|
||||
}}
|
||||
/>
|
||||
{item.categoryName}
|
||||
</td>
|
||||
<td>{formatAmount(item.amount)}</td>
|
||||
<td className="td-center">{item.txCount}</td>
|
||||
<td className="td-center">
|
||||
{(item.share * 100).toFixed(1)}%
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user