feat: add portfolio valuation history
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@family-budget/frontend",
|
||||
"version": "0.13.0",
|
||||
"version": "0.14.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ImportBrokerReportResponse, ImportPortfolioResponse, PortfolioFile, PortfolioOverviewResponse } from '@family-budget/shared';
|
||||
import type { ImportBrokerReportResponse, ImportPortfolioResponse, PortfolioFile, PortfolioHistoryResponse, PortfolioOverviewResponse } from '@family-budget/shared';
|
||||
import { api } from './client';
|
||||
|
||||
export function importPortfolio(data: PortfolioFile): Promise<ImportPortfolioResponse> {
|
||||
@@ -14,3 +14,7 @@ export function importBrokerReport(file: File): Promise<ImportBrokerReportRespon
|
||||
export function getPortfolioOverview(): Promise<PortfolioOverviewResponse> {
|
||||
return api.get('/api/portfolio');
|
||||
}
|
||||
|
||||
export function getPortfolioHistory(): Promise<PortfolioHistoryResponse> {
|
||||
return api.get('/api/portfolio/history');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { PortfolioOverviewResponse } from '@family-budget/shared';
|
||||
import { getPortfolioOverview } from '../api/portfolio';
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
import type { PortfolioHistoryResponse, PortfolioOverviewResponse } from '@family-budget/shared';
|
||||
import { getPortfolioHistory, getPortfolioOverview } from '../api/portfolio';
|
||||
import { formatDate } from '../utils/format';
|
||||
|
||||
const money = new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB', minimumFractionDigits: 2 });
|
||||
@@ -8,9 +9,11 @@ const number = new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 6 });
|
||||
|
||||
export function PortfolioPage() {
|
||||
const [data, setData] = useState<PortfolioOverviewResponse | null>(null);
|
||||
const [history, setHistory] = useState<PortfolioHistoryResponse | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
getPortfolioOverview().then(setData).catch(() => {});
|
||||
getPortfolioHistory().then(setHistory).catch(() => {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -50,6 +53,28 @@ export function PortfolioPage() {
|
||||
)}
|
||||
</section>
|
||||
))}
|
||||
|
||||
{history?.accounts.map((account) => account.points.length > 1 && (
|
||||
<section className="portfolio" key={`history-${account.accountId}`} aria-labelledby={`history-${account.accountId}`}>
|
||||
<div className="portfolio__header">
|
||||
<div>
|
||||
<h2 id={`history-${account.accountId}`} className="portfolio__title">Динамика · {account.accountName}</h2>
|
||||
<p className="portfolio__meta">Стоимость по загруженным отчётам</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="chart-card portfolio__chart">
|
||||
<ResponsiveContainer width="100%" height={280}>
|
||||
<LineChart data={account.points.map((point) => ({ date: point.reportPeriodTo, value: point.totalValuation === null ? null : Number(point.totalValuation) }))}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="var(--color-border)" vertical={false} />
|
||||
<XAxis dataKey="date" tickFormatter={(value: string) => formatDate(value)} fontSize={12} stroke="var(--color-text-secondary)" tickLine={false} axisLine={false} />
|
||||
<YAxis tickFormatter={(value: number) => `${Math.round(value / 1000)}к`} fontSize={12} stroke="var(--color-text-secondary)" tickLine={false} axisLine={false} />
|
||||
<Tooltip labelFormatter={(value) => formatDate(String(value))} formatter={(value) => value == null ? '—' : money.format(Number(value))} />
|
||||
<Line type="monotone" dataKey="value" name="Стоимость" stroke="var(--color-primary)" strokeWidth={2} dot={{ r: 3 }} connectNulls />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -368,6 +368,11 @@ button {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.portfolio__chart {
|
||||
min-height: 280px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
Forms, buttons, badges
|
||||
================================================================ */
|
||||
|
||||
Reference in New Issue
Block a user