78 lines
2.0 KiB
TypeScript
78 lines
2.0 KiB
TypeScript
'use client';
|
||
|
||
import {
|
||
CartesianGrid,
|
||
Line,
|
||
LineChart,
|
||
ResponsiveContainer,
|
||
Tooltip,
|
||
XAxis,
|
||
YAxis,
|
||
} from 'recharts';
|
||
|
||
type ChartPoint = {
|
||
time: string;
|
||
odds: number;
|
||
bookmaker: string;
|
||
};
|
||
|
||
type GroupedData = {
|
||
time: string;
|
||
[bookmaker: string]: string | number;
|
||
};
|
||
|
||
function normalizeForChart(points: ChartPoint[]): GroupedData[] {
|
||
const map = new Map<string, GroupedData>();
|
||
|
||
for (const row of points) {
|
||
const current = map.get(row.time) || { time: row.time };
|
||
current[row.bookmaker] = row.odds;
|
||
map.set(row.time, current);
|
||
}
|
||
|
||
return Array.from(map.values()).sort((a, b) =>
|
||
a.time.localeCompare(b.time),
|
||
);
|
||
}
|
||
|
||
type Props = {
|
||
data: ChartPoint[];
|
||
};
|
||
|
||
export function OddsLineMovementChart({ data }: Props) {
|
||
const bookmakers = Array.from(new Set(data.map((row) => row.bookmaker)));
|
||
const lines = normalizeForChart(data);
|
||
|
||
const palette = ['#b83822', '#7a4a2c', '#dcb53b', '#5f4031', '#8c5b38'];
|
||
|
||
return (
|
||
<div className="panel-glow rounded-2xl p-4">
|
||
<h3 className="dot-matrix text-xl text-[#7d2a15]">賠率走勢:小數賠率變化</h3>
|
||
<div className="mt-3 h-72 w-full">
|
||
<ResponsiveContainer width="100%" height="100%">
|
||
<LineChart data={lines} margin={{ top: 12, right: 24, bottom: 12, left: 6 }}>
|
||
<CartesianGrid strokeDasharray="4 4" stroke="#eadcb9" />
|
||
<XAxis dataKey="time" tick={{ fill: '#6d4d39' }} />
|
||
<YAxis tick={{ fill: '#6d4d39' }} />
|
||
<Tooltip
|
||
contentStyle={{ background: '#fff8e6', borderColor: '#d8b58c' }}
|
||
itemStyle={{ color: '#5f4031' }}
|
||
/>
|
||
{bookmakers.map((bookmaker, idx) => (
|
||
<Line
|
||
key={bookmaker}
|
||
type="monotone"
|
||
dataKey={bookmaker}
|
||
stroke={palette[idx % palette.length]}
|
||
strokeWidth={2.3}
|
||
dot={false}
|
||
/>
|
||
))}
|
||
</LineChart>
|
||
</ResponsiveContainer>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|