import { useState, useEffect } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, ReferenceLine, } from 'recharts'; import { PriceHistory } from '../api/client'; const getThemeColors = () => { const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; return { grid: isDark ? '#334155' : '#e2e8f0', text: isDark ? '#64748b' : '#94a3b8', tooltip: isDark ? '#1e293b' : 'white', tooltipBorder: isDark ? '#334155' : '#e2e8f0', }; }; interface PriceChartProps { prices: PriceHistory[]; currency: string; onRangeChange?: (days: number | undefined) => void; } const DATE_RANGES = [ { value: 7, label: '7 days' }, { value: 30, label: '30 days' }, { value: 90, label: '90 days' }, { value: undefined, label: 'All time' }, ]; export default function PriceChart({ prices, currency, onRangeChange, }: PriceChartProps) { const [selectedRange, setSelectedRange] = useState(30); const [themeColors, setThemeColors] = useState(getThemeColors); useEffect(() => { const observer = new MutationObserver(() => { setThemeColors(getThemeColors()); }); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'], }); return () => observer.disconnect(); }, []); const handleRangeChange = (days: number | undefined) => { setSelectedRange(days); onRangeChange?.(days); }; const currencySymbol = currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : currency === 'CHF' ? 'CHF ' : '$'; const chartData = prices.map((p) => ({ date: new Date(p.recorded_at).getTime(), price: typeof p.price === 'string' ? parseFloat(p.price) : p.price, })); const priceValues = chartData.map((d) => d.price).filter((p) => !isNaN(p)); const minPrice = priceValues.length > 0 ? Math.min(...priceValues) : 0; const maxPrice = priceValues.length > 0 ? Math.max(...priceValues) : 0; const avgPrice = priceValues.length > 0 ? priceValues.reduce((sum, p) => sum + p, 0) / priceValues.length : 0; const formatDate = (timestamp: number) => { const date = new Date(timestamp); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); }; const formatPrice = (value: number) => { if (value === null || value === undefined || isNaN(value)) return 'N/A'; return `${currencySymbol}${value.toFixed(2)}`; }; if (prices.length === 0) { return (

No price history available yet.

); } return (

Price History

{DATE_RANGES.map((range) => ( ))}
[formatPrice(value), 'Price']} labelFormatter={(label) => new Date(label).toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }) } contentStyle={{ background: themeColors.tooltip, border: `1px solid ${themeColors.tooltipBorder}`, borderRadius: '0.5rem', boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)', }} />
Lowest
{formatPrice(minPrice)}
Average
{formatPrice(avgPrice)}
Highest
{formatPrice(maxPrice)}
); }