Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 16x 16x 16x 16x 16x 16x 16x 16x 16x 1x 1x 1x 1x 1x 16x 16x 16x 16x 16x 16x 16x 16x 11x 11x 5x 16x 3x 3x 3x 3x 3x 3x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x | /**
* @file PriceChart.tsx
* @description
* Price trend line chart component.
* Renders recharts LineChart with date/price formatting.
*/
import { Box, Skeleton } from '@mui/material';
import { useTheme as useMuiTheme } from '@mui/material/styles';
import { useMemo } from 'react';
import { ResponsiveContainer, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, Line } from 'recharts';
import type { PricePoint } from '../../../../api/analytics';
import type { DateFormat } from '../../../../context/settings/SettingsContext.types';
import { formatDate, formatNumber } from '../../../../utils/formatters';
/**
* Props for PriceChart
*/
export interface PriceChartProps {
/** Price data points to render */
data: PricePoint[];
/** Whether chart is loading */
isLoading: boolean;
/** Date format preference (DD.MM.YYYY, YYYY-MM-DD, MM/DD/YYYY) */
dateFormat: DateFormat;
/** Number format preference (DE, EN_US, etc) */
numberFormat: 'DE' | 'EN_US';
}
/**
* Price trend line chart
* Displays price movement over time with formatted axes
*/
export function PriceChart({
data,
isLoading,
dateFormat,
numberFormat,
}: PriceChartProps) {
const muiTheme = useMuiTheme();
// Format date labels for x-axis
const formatDateLabel = (value: string | number) => {
const str = String(value);
// dateFormat is already typed as DateFormat from props
const formatted = formatDate(str, dateFormat);
return formatted || str;
};
// Sort data by date for deterministic rendering
const sortedData = useMemo(
() => [...data].sort((a, b) => (a?.date ?? '').localeCompare(b?.date ?? '')),
[data]
);
if (isLoading) {
return <Skeleton variant="rounded" height={220} />;
}
if (sortedData.length === 0) {
return (
<Box sx={{ height: 220, display: 'grid', placeItems: 'center', color: 'text.secondary' }}>
No price data available
</Box>
);
}
return (
<Box sx={{ height: 260 }}>
<ResponsiveContainer width="100%" height="100%">
<LineChart data={sortedData} margin={{ top: 8, right: 16, left: 8, bottom: 8 }}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" tickFormatter={formatDateLabel} />
<YAxis
domain={['auto', 'auto']}
tickFormatter={(value) => formatNumber(Number(value), numberFormat, 2)}
/>
<Tooltip
labelFormatter={(value) => formatDateLabel(value as string)}
formatter={(value: number | string) =>
typeof value === 'number'
? formatNumber(value, numberFormat, 2)
: value
}
/>
<Line
type="monotone"
dataKey="price"
stroke={muiTheme.palette.primary.main}
strokeWidth={2}
dot={{ r: 2 }}
activeDot={{ r: 4 }}
connectNulls
isAnimationActive={false}
/>
</LineChart>
</ResponsiveContainer>
</Box>
);
}
|