feat(frontend): add dashboard charts
- Sparkline for summary cards - AreaChart for asset trends - DonutChart for sector allocation - BarChart for portfolio comparison Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
eb3ce0e6e7
commit
4f432fb85c
@ -2,35 +2,110 @@
|
||||
|
||||
import { DashboardLayout } from '@/components/layout/dashboard-layout';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Sparkline, AreaChart, DonutChart, BarChart } from '@/components/charts';
|
||||
import { Wallet, TrendingUp, Briefcase, RefreshCw } from 'lucide-react';
|
||||
|
||||
// Sample data for sparklines
|
||||
const totalAssetSparkline = [
|
||||
{ value: 9500000 },
|
||||
{ value: 9800000 },
|
||||
{ value: 10000000 },
|
||||
{ value: 9700000 },
|
||||
{ value: 10200000 },
|
||||
{ value: 10500000 },
|
||||
{ value: 10800000 },
|
||||
];
|
||||
|
||||
const returnSparkline = [
|
||||
{ value: 2.5 },
|
||||
{ value: 3.1 },
|
||||
{ value: 2.8 },
|
||||
{ value: 4.2 },
|
||||
{ value: 5.1 },
|
||||
{ value: 4.8 },
|
||||
{ value: 5.5 },
|
||||
];
|
||||
|
||||
// Sample data for asset trend chart
|
||||
const assetTrendData = [
|
||||
{ date: '2024-01', value: 10000000 },
|
||||
{ date: '2024-02', value: 10500000 },
|
||||
{ date: '2024-03', value: 10200000 },
|
||||
{ date: '2024-04', value: 10800000 },
|
||||
{ date: '2024-05', value: 11200000 },
|
||||
{ date: '2024-06', value: 11000000 },
|
||||
{ date: '2024-07', value: 11500000 },
|
||||
{ date: '2024-08', value: 12000000 },
|
||||
{ date: '2024-09', value: 11800000 },
|
||||
{ date: '2024-10', value: 12500000 },
|
||||
{ date: '2024-11', value: 13000000 },
|
||||
{ date: '2024-12', value: 13500000 },
|
||||
];
|
||||
|
||||
// Sample data for sector allocation
|
||||
const sectorData = [
|
||||
{ name: '기술', value: 40, color: '#3b82f6' },
|
||||
{ name: '금융', value: 30, color: '#10b981' },
|
||||
{ name: '헬스케어', value: 20, color: '#f59e0b' },
|
||||
{ name: '기타', value: 10, color: '#6b7280' },
|
||||
];
|
||||
|
||||
// Sample data for portfolio comparison
|
||||
const portfolioComparisonData = [
|
||||
{ name: '포트폴리오 A', value: 12.5 },
|
||||
{ name: '포트폴리오 B', value: 8.3 },
|
||||
{ name: '포트폴리오 C', value: -2.1 },
|
||||
{ name: 'KOSPI', value: 5.7 },
|
||||
{ name: 'S&P 500', value: 15.2 },
|
||||
];
|
||||
|
||||
const summaryCards = [
|
||||
{
|
||||
title: '총 자산',
|
||||
value: '---',
|
||||
value: '₩135,000,000',
|
||||
description: '전체 포트폴리오 가치',
|
||||
icon: Wallet,
|
||||
sparklineData: totalAssetSparkline,
|
||||
sparklineColor: '#3b82f6',
|
||||
},
|
||||
{
|
||||
title: '총 수익률',
|
||||
value: '---%',
|
||||
value: '+35.0%',
|
||||
description: '전체 수익률',
|
||||
icon: TrendingUp,
|
||||
sparklineData: returnSparkline,
|
||||
sparklineColor: '#10b981',
|
||||
},
|
||||
{
|
||||
title: '포트폴리오',
|
||||
value: '-',
|
||||
value: '3',
|
||||
description: '활성 포트폴리오 수',
|
||||
icon: Briefcase,
|
||||
sparklineData: null,
|
||||
sparklineColor: null,
|
||||
},
|
||||
{
|
||||
title: '리밸런싱',
|
||||
value: '-',
|
||||
value: '2',
|
||||
description: '예정된 리밸런싱',
|
||||
icon: RefreshCw,
|
||||
sparklineData: null,
|
||||
sparklineColor: null,
|
||||
},
|
||||
];
|
||||
|
||||
const formatKRW = (value: number) => {
|
||||
if (value >= 100000000) {
|
||||
return `${(value / 100000000).toFixed(1)}억`;
|
||||
}
|
||||
if (value >= 10000) {
|
||||
return `${(value / 10000).toFixed(0)}만`;
|
||||
}
|
||||
return value.toLocaleString();
|
||||
};
|
||||
|
||||
const formatPercent = (value: number) => `${value.toFixed(1)}%`;
|
||||
|
||||
export default function DashboardPage() {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
@ -52,22 +127,34 @@ export default function DashboardPage() {
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{card.description}
|
||||
</p>
|
||||
{card.sparklineData && (
|
||||
<Sparkline
|
||||
data={card.sparklineData}
|
||||
color={card.sparklineColor || '#3b82f6'}
|
||||
height={32}
|
||||
className="mt-2"
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Chart Placeholders */}
|
||||
{/* Main Charts */}
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>포트폴리오 성과</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center justify-center h-64 bg-muted/50 rounded-lg">
|
||||
<p className="text-muted-foreground">차트 영역</p>
|
||||
</div>
|
||||
<AreaChart
|
||||
data={assetTrendData}
|
||||
height={280}
|
||||
color="#3b82f6"
|
||||
formatValue={formatKRW}
|
||||
showLegend={false}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -76,22 +163,30 @@ export default function DashboardPage() {
|
||||
<CardTitle>자산 배분</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center justify-center h-64 bg-muted/50 rounded-lg">
|
||||
<p className="text-muted-foreground">차트 영역</p>
|
||||
</div>
|
||||
<DonutChart
|
||||
data={sectorData}
|
||||
height={280}
|
||||
innerRadius={50}
|
||||
outerRadius={90}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Secondary Charts */}
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>최근 거래</CardTitle>
|
||||
<CardTitle>포트폴리오 비교</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center justify-center h-48 bg-muted/50 rounded-lg">
|
||||
<p className="text-muted-foreground">거래 내역 영역</p>
|
||||
</div>
|
||||
<BarChart
|
||||
data={portfolioComparisonData}
|
||||
height={240}
|
||||
layout="horizontal"
|
||||
formatValue={formatPercent}
|
||||
colorByValue={true}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -100,8 +195,28 @@ export default function DashboardPage() {
|
||||
<CardTitle>알림</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center justify-center h-48 bg-muted/50 rounded-lg">
|
||||
<p className="text-muted-foreground">알림 영역</p>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-muted/50">
|
||||
<RefreshCw className="h-4 w-4 text-blue-500" />
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium">리밸런싱 예정</p>
|
||||
<p className="text-xs text-muted-foreground">포트폴리오 A - 2일 후</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-muted/50">
|
||||
<TrendingUp className="h-4 w-4 text-green-500" />
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium">목표 수익률 달성</p>
|
||||
<p className="text-xs text-muted-foreground">포트폴리오 B - 오늘</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-muted/50">
|
||||
<Briefcase className="h-4 w-4 text-amber-500" />
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium">배당금 입금</p>
|
||||
<p className="text-xs text-muted-foreground">삼성전자 - 3일 후</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
118
frontend/src/components/charts/area-chart.tsx
Normal file
118
frontend/src/components/charts/area-chart.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Area,
|
||||
AreaChart as RechartsAreaChart,
|
||||
CartesianGrid,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts';
|
||||
|
||||
interface AreaChartData {
|
||||
date: string;
|
||||
value: number;
|
||||
[key: string]: string | number;
|
||||
}
|
||||
|
||||
interface AreaChartProps {
|
||||
data: AreaChartData[];
|
||||
dataKey?: string;
|
||||
xAxisKey?: string;
|
||||
color?: string;
|
||||
height?: number;
|
||||
showLegend?: boolean;
|
||||
showGrid?: boolean;
|
||||
formatValue?: (value: number) => string;
|
||||
formatXAxis?: (value: string) => string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function AreaChart({
|
||||
data,
|
||||
dataKey = 'value',
|
||||
xAxisKey = 'date',
|
||||
color = '#3b82f6',
|
||||
height = 300,
|
||||
showLegend = true,
|
||||
showGrid = true,
|
||||
formatValue = (value) => value.toLocaleString(),
|
||||
formatXAxis = (value) => value,
|
||||
className,
|
||||
}: AreaChartProps) {
|
||||
if (!data || data.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64 bg-muted/50 rounded-lg">
|
||||
<p className="text-muted-foreground">데이터가 없습니다</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} style={{ height }}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<RechartsAreaChart
|
||||
data={data}
|
||||
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor={color} stopOpacity={0.3} />
|
||||
<stop offset="95%" stopColor={color} stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
{showGrid && (
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
stroke="hsl(var(--border))"
|
||||
vertical={false}
|
||||
/>
|
||||
)}
|
||||
<XAxis
|
||||
dataKey={xAxisKey}
|
||||
tickFormatter={formatXAxis}
|
||||
stroke="hsl(var(--muted-foreground))"
|
||||
fontSize={12}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
<YAxis
|
||||
tickFormatter={formatValue}
|
||||
stroke="hsl(var(--muted-foreground))"
|
||||
fontSize={12}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
width={80}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'hsl(var(--popover))',
|
||||
border: '1px solid hsl(var(--border))',
|
||||
borderRadius: '8px',
|
||||
color: 'hsl(var(--popover-foreground))',
|
||||
}}
|
||||
labelStyle={{ color: 'hsl(var(--popover-foreground))' }}
|
||||
formatter={(value) => [formatValue(value as number), '자산']}
|
||||
/>
|
||||
{showLegend && (
|
||||
<Legend
|
||||
wrapperStyle={{
|
||||
color: 'hsl(var(--foreground))',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey={dataKey}
|
||||
stroke={color}
|
||||
strokeWidth={2}
|
||||
fill="url(#areaGradient)"
|
||||
name="자산 가치"
|
||||
/>
|
||||
</RechartsAreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
155
frontend/src/components/charts/bar-chart.tsx
Normal file
155
frontend/src/components/charts/bar-chart.tsx
Normal file
@ -0,0 +1,155 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Bar,
|
||||
BarChart as RechartsBarChart,
|
||||
CartesianGrid,
|
||||
Cell,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts';
|
||||
|
||||
interface BarChartData {
|
||||
name: string;
|
||||
value: number;
|
||||
[key: string]: string | number;
|
||||
}
|
||||
|
||||
interface BarChartProps {
|
||||
data: BarChartData[];
|
||||
dataKey?: string;
|
||||
nameKey?: string;
|
||||
height?: number;
|
||||
layout?: 'horizontal' | 'vertical';
|
||||
showLegend?: boolean;
|
||||
showGrid?: boolean;
|
||||
colorByValue?: boolean;
|
||||
positiveColor?: string;
|
||||
negativeColor?: string;
|
||||
defaultColor?: string;
|
||||
formatValue?: (value: number) => string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function BarChart({
|
||||
data,
|
||||
dataKey = 'value',
|
||||
nameKey = 'name',
|
||||
height = 300,
|
||||
layout = 'vertical',
|
||||
showLegend = false,
|
||||
showGrid = true,
|
||||
colorByValue = true,
|
||||
positiveColor = '#10b981',
|
||||
negativeColor = '#ef4444',
|
||||
defaultColor = '#3b82f6',
|
||||
formatValue = (value) => value.toLocaleString(),
|
||||
className,
|
||||
}: BarChartProps) {
|
||||
if (!data || data.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64 bg-muted/50 rounded-lg">
|
||||
<p className="text-muted-foreground">데이터가 없습니다</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getBarColor = (value: number) => {
|
||||
if (!colorByValue) return defaultColor;
|
||||
return value >= 0 ? positiveColor : negativeColor;
|
||||
};
|
||||
|
||||
const isHorizontal = layout === 'horizontal';
|
||||
|
||||
return (
|
||||
<div className={className} style={{ height }}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<RechartsBarChart
|
||||
data={data}
|
||||
layout={isHorizontal ? 'vertical' : 'horizontal'}
|
||||
margin={{ top: 10, right: 30, left: isHorizontal ? 80 : 0, bottom: 0 }}
|
||||
>
|
||||
{showGrid && (
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
stroke="hsl(var(--border))"
|
||||
horizontal={!isHorizontal}
|
||||
vertical={isHorizontal}
|
||||
/>
|
||||
)}
|
||||
{isHorizontal ? (
|
||||
<>
|
||||
<XAxis
|
||||
type="number"
|
||||
tickFormatter={formatValue}
|
||||
stroke="hsl(var(--muted-foreground))"
|
||||
fontSize={12}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
<YAxis
|
||||
type="category"
|
||||
dataKey={nameKey}
|
||||
stroke="hsl(var(--muted-foreground))"
|
||||
fontSize={12}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
width={80}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<XAxis
|
||||
dataKey={nameKey}
|
||||
stroke="hsl(var(--muted-foreground))"
|
||||
fontSize={12}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
<YAxis
|
||||
tickFormatter={formatValue}
|
||||
stroke="hsl(var(--muted-foreground))"
|
||||
fontSize={12}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
width={60}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'hsl(var(--popover))',
|
||||
border: '1px solid hsl(var(--border))',
|
||||
borderRadius: '8px',
|
||||
color: 'hsl(var(--popover-foreground))',
|
||||
}}
|
||||
formatter={(value) => [formatValue(value as number), '수익률']}
|
||||
cursor={{ fill: 'hsl(var(--muted))', opacity: 0.3 }}
|
||||
/>
|
||||
{showLegend && (
|
||||
<Legend
|
||||
wrapperStyle={{
|
||||
color: 'hsl(var(--foreground))',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Bar
|
||||
dataKey={dataKey}
|
||||
radius={[4, 4, 4, 4]}
|
||||
name="수익률"
|
||||
>
|
||||
{data.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={getBarColor(entry[dataKey] as number)}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
</RechartsBarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
106
frontend/src/components/charts/donut-chart.tsx
Normal file
106
frontend/src/components/charts/donut-chart.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Cell,
|
||||
Legend,
|
||||
Pie,
|
||||
PieChart,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
} from 'recharts';
|
||||
|
||||
interface DonutChartData {
|
||||
name: string;
|
||||
value: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
interface DonutChartProps {
|
||||
data: DonutChartData[];
|
||||
height?: number;
|
||||
innerRadius?: number;
|
||||
outerRadius?: number;
|
||||
showLegend?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function DonutChart({
|
||||
data,
|
||||
height = 300,
|
||||
innerRadius = 60,
|
||||
outerRadius = 100,
|
||||
showLegend = true,
|
||||
className,
|
||||
}: DonutChartProps) {
|
||||
if (!data || data.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64 bg-muted/50 rounded-lg">
|
||||
<p className="text-muted-foreground">데이터가 없습니다</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const total = data.reduce((sum, item) => sum + item.value, 0);
|
||||
|
||||
const renderLegend = () => {
|
||||
return (
|
||||
<ul className="flex flex-col gap-2 text-sm">
|
||||
{data.map((entry, index) => (
|
||||
<li key={`legend-${index}`} className="flex items-center gap-2">
|
||||
<span
|
||||
className="w-3 h-3 rounded-full"
|
||||
style={{ backgroundColor: entry.color }}
|
||||
/>
|
||||
<span className="text-foreground">{entry.name}</span>
|
||||
<span className="text-muted-foreground ml-auto">
|
||||
{((entry.value / total) * 100).toFixed(1)}%
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className} style={{ height }}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={data}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
innerRadius={innerRadius}
|
||||
outerRadius={outerRadius}
|
||||
paddingAngle={2}
|
||||
dataKey="value"
|
||||
stroke="none"
|
||||
>
|
||||
{data.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'hsl(var(--popover))',
|
||||
border: '1px solid hsl(var(--border))',
|
||||
borderRadius: '8px',
|
||||
color: 'hsl(var(--popover-foreground))',
|
||||
}}
|
||||
formatter={(value, name) => [
|
||||
`${(((value as number) / total) * 100).toFixed(1)}%`,
|
||||
name,
|
||||
]}
|
||||
/>
|
||||
{showLegend && (
|
||||
<Legend
|
||||
content={renderLegend}
|
||||
layout="vertical"
|
||||
align="right"
|
||||
verticalAlign="middle"
|
||||
/>
|
||||
)}
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
4
frontend/src/components/charts/index.ts
Normal file
4
frontend/src/components/charts/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export { Sparkline } from './sparkline';
|
||||
export { AreaChart } from './area-chart';
|
||||
export { DonutChart } from './donut-chart';
|
||||
export { BarChart } from './bar-chart';
|
||||
48
frontend/src/components/charts/sparkline.tsx
Normal file
48
frontend/src/components/charts/sparkline.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
'use client';
|
||||
|
||||
import { Area, AreaChart, ResponsiveContainer } from 'recharts';
|
||||
|
||||
interface SparklineData {
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface SparklineProps {
|
||||
data: SparklineData[];
|
||||
color?: string;
|
||||
height?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Sparkline({
|
||||
data,
|
||||
color = '#3b82f6',
|
||||
height = 40,
|
||||
className,
|
||||
}: SparklineProps) {
|
||||
if (!data || data.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} style={{ height }}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<AreaChart data={data} margin={{ top: 0, right: 0, left: 0, bottom: 0 }}>
|
||||
<defs>
|
||||
<linearGradient id={`sparkline-gradient-${color.replace('#', '')}`} x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor={color} stopOpacity={0.3} />
|
||||
<stop offset="100%" stopColor={color} stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="value"
|
||||
stroke={color}
|
||||
strokeWidth={1.5}
|
||||
fill={`url(#sparkline-gradient-${color.replace('#', '')})`}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user