Fix title-race chart: persist the diary, dedupe restarts, date axis
The chart looked broken because its data was: history lived in memory, so every deploy wiped it (yesterday's points were gone), and each boot plus the +5min refit appended a fresh point labeled with the same last result — leaving two identical flat segments on an index axis. - model_history table: every diary point persists with the finished- result signature it was computed from; restored at boot - points only append when the finished set actually changes — refit and boot recomputes update the snapshot without spamming the diary - one-time rebuild when nothing is persisted: replay finished results in kickoff order, re-running the sim after each with later results masked, so the historical points are recreated faithfully - chart x-axis shows localized dates instead of raw indices Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid, Legend } from 'recharts';
|
||||
import { teamFlag } from '@/lib/teams';
|
||||
import { useFormat } from '@/lib/i18n';
|
||||
import type { OddsHistoryPoint } from '@/lib/types';
|
||||
|
||||
const PALETTE = [
|
||||
@@ -15,23 +16,32 @@ const PALETTE = [
|
||||
/** Championship odds over time — one line per top contender, a point per result
|
||||
* that moved the model. */
|
||||
export function OddsChart({ history }: { history: OddsHistoryPoint[] }) {
|
||||
const { locale } = useFormat();
|
||||
const { data, teams } = useMemo(() => {
|
||||
const last = history[history.length - 1];
|
||||
const teams = (last?.top ?? []).slice(0, 6).map((t) => t.team);
|
||||
const tag = locale === 'de' ? 'de-DE' : 'en-GB';
|
||||
const data = history.map((h, i) => {
|
||||
const row: Record<string, number | string> = { i: i + 1, label: h.label };
|
||||
const day = new Date(h.t).toLocaleDateString(tag, { day: 'numeric', month: 'short' });
|
||||
const row: Record<string, number | string> = { i: i + 1, label: h.label, day };
|
||||
for (const t of h.top) if (teams.includes(t.team)) row[t.team] = Math.round(t.champion * 1000) / 10;
|
||||
return row;
|
||||
});
|
||||
return { data, teams };
|
||||
}, [history]);
|
||||
}, [history, locale]);
|
||||
|
||||
return (
|
||||
<div className="h-72 w-full">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart data={data} margin={{ top: 8, right: 16, bottom: 4, left: -8 }}>
|
||||
<CartesianGrid stroke="var(--app-line)" strokeDasharray="3 3" vertical={false} />
|
||||
<XAxis dataKey="i" tick={{ fill: 'var(--app-faint)', fontSize: 11 }} stroke="var(--app-line)" />
|
||||
<XAxis
|
||||
dataKey="i"
|
||||
tick={{ fill: 'var(--app-faint)', fontSize: 11 }}
|
||||
stroke="var(--app-line)"
|
||||
tickFormatter={(i) => String(data[Number(i) - 1]?.day ?? '')}
|
||||
minTickGap={28}
|
||||
/>
|
||||
<YAxis
|
||||
tick={{ fill: 'var(--app-faint)', fontSize: 11 }}
|
||||
stroke="var(--app-line)"
|
||||
|
||||
Reference in New Issue
Block a user