Starters-on-pitch, chart movers sort, pre-filled what-if bracket, vs/@ fix
- Finished matches show the STARTING XIs on the pitch (red markers with the minute on everyone subbed off) with 'Subbed on' / 'Subbed off' lists below, minutes and ratings included; live matches keep the current-XI view that swaps substitutes in - Title-race chart gets a sort toggle, defaulting to 'Biggest movers' (the teams whose odds changed most at the latest update — what's relevant now) with 'Title favorites' as the alternative - Entering what-if on the bracket now pre-fills the model's most likely tournament: group slots seeded from win-group/advance odds (best- thirds assigned to their allowed slots via backtracking), every pairing picked by the model — a complete overview to edit by tapping; Reset returns to the projection - Team fixtures drop the US 'vs/@' convention: always 'vs' plus an H/A badge with a tooltip Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid, Legend } from 'recharts';
|
||||
import { teamFlag } from '@/lib/teams';
|
||||
import { useFormat } from '@/lib/i18n';
|
||||
import { useFormat, useT } from '@/lib/i18n';
|
||||
import type { OddsHistoryPoint } from '@/lib/types';
|
||||
|
||||
const PALETTE = [
|
||||
@@ -15,11 +15,25 @@ const PALETTE = [
|
||||
|
||||
/** Championship odds over time — one line per top contender, a point per result
|
||||
* that moved the model. */
|
||||
type ChartSort = 'movers' | 'leaders';
|
||||
|
||||
export function OddsChart({ history }: { history: OddsHistoryPoint[] }) {
|
||||
const { locale } = useFormat();
|
||||
const t = useT();
|
||||
// Default to the teams whose odds moved most at the latest update — what
|
||||
// changed just now is more interesting than a static favorites list.
|
||||
const [sort, setSort] = useState<ChartSort>('movers');
|
||||
const { data, teams } = useMemo(() => {
|
||||
const last = history[history.length - 1];
|
||||
const teams = (last?.top ?? []).slice(0, 6).map((t) => t.team);
|
||||
const prev = history[history.length - 2];
|
||||
const latest = last?.top ?? [];
|
||||
let picked = latest.slice(0, 6);
|
||||
if (sort === 'movers' && prev) {
|
||||
const before = new Map(prev.top.map((x) => [x.team, x.champion]));
|
||||
const delta = (x: { team: string; champion: number }) => Math.abs(x.champion - (before.get(x.team) ?? x.champion));
|
||||
picked = [...latest].sort((a, b) => delta(b) - delta(a) || b.champion - a.champion).slice(0, 6);
|
||||
}
|
||||
const teams = picked.map((x) => x.team);
|
||||
const tag = locale === 'de' ? 'de-DE' : 'en-GB';
|
||||
const data = history.map((h, i) => {
|
||||
const day = new Date(h.t).toLocaleDateString(tag, { day: 'numeric', month: 'short' });
|
||||
@@ -28,10 +42,25 @@ export function OddsChart({ history }: { history: OddsHistoryPoint[] }) {
|
||||
return row;
|
||||
});
|
||||
return { data, teams };
|
||||
}, [history, locale]);
|
||||
}, [history, locale, sort]);
|
||||
|
||||
return (
|
||||
<div className="h-72 w-full">
|
||||
<div className="w-full">
|
||||
<div className="mb-2 flex justify-end">
|
||||
<div className="flex overflow-hidden rounded-lg border border-line text-xs font-medium">
|
||||
{([['movers', t.predict.titleRace.sortMovers], ['leaders', t.predict.titleRace.sortLeaders]] as const).map(([v, label]) => (
|
||||
<button
|
||||
key={v}
|
||||
type="button"
|
||||
onClick={() => setSort(v)}
|
||||
className={v === sort ? 'bg-accent-glow px-3 py-1 font-bold text-accent' : 'px-3 py-1 text-muted hover:bg-elevated hover:text-ink'}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<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} />
|
||||
@@ -74,6 +103,7 @@ export function OddsChart({ history }: { history: OddsHistoryPoint[] }) {
|
||||
))}
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user