Match center v2: xG race, shot map, attack momentum, weather, officials

The captured FotMob/FIFA/Open-Meteo data becomes visible. Match pages
now carry: a cumulative xG race with goal dots and team totals, a full-
pitch shot map (each side attacking its own end, dots sized by xG,
goals filled), the 90-minute attack-momentum chart, player-of-the-match
with rating, the official info line (stadium · attendance · referee),
and a kickoff-hour weather line for upcoming matches with an altitude
chip above 1,000m. All hand-rolled SVG on the existing token
conventions — design-ready for the upcoming visual overhaul. EN/DE
included, with an honest 'xG and ratings are FotMob estimates' note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 10:10:35 +02:00
parent b2f680906b
commit b823bef2d0
7 changed files with 358 additions and 2 deletions
+8
View File
@@ -106,6 +106,14 @@ export const de: Dict = {
},
lineups: "Aufstellungen",
keyPlayers: "Schlüsselspieler (beste Torschützen aller Zeiten)",
xgRace: "xG-Rennen",
shotMap: "Schusskarte",
momentumTitle: "Angriffsdruck",
weatherLine: "{temp} °C · {precip} % Regen · {wind} km/h Wind",
altitude: "{m} m Höhe",
attendanceLine: "{stadium} · {n} Zuschauer · Schiedsrichter {ref}",
potm: "Spieler des Spiels",
xgAttribution: "xG und Bewertungen sind FotMob-Schätzungen.",
},
compare: {
title: "Vergleich",
+8
View File
@@ -105,6 +105,14 @@ export const en = {
},
lineups: "Lineups",
keyPlayers: "Key players (all-time top scorers)",
xgRace: "xG race",
shotMap: "Shot map",
momentumTitle: "Attack momentum",
weatherLine: "{temp}°C · {precip}% rain · {wind} km/h wind",
altitude: "{m} m altitude",
attendanceLine: "{stadium} · {n} spectators · referee {ref}",
potm: "Player of the match",
xgAttribution: "xG and ratings are FotMob estimates.",
},
compare: {
title: "Compare",
+27
View File
@@ -164,6 +164,33 @@ export interface MatchPreview {
updatedAt: number | null;
}
// ---- rich match data (/api/match-rich — FotMob/FIFA/Open-Meteo capture) ----
/** One captured shot. FotMob pitch coords ≈105×68 m, attacking toward x=105. */
export interface RichShot {
/** true = home, false = away, null when the side couldn't be resolved. */
isHome: boolean | null;
player: string;
x: number | null;
y: number | null;
min: number | null;
xg: number | null;
xgot: number | null;
/** FotMob event type: 'Goal' means scored; 'AttemptSaved' | 'Miss' | 'Post' otherwise. */
type: string;
situation: string;
}
export interface MatchRich {
shots: RichShot[];
xg: { home: number; away: number } | null;
/** Attack-momentum curve; positive values = home pressure. */
momentum: { minute: number; value: number }[];
potm: { name: string; rating: string | null } | null;
info: { attendance: number | null; stadium: string; referee: string | null } | null;
weather: { tempC: number | null; precipProbPct: number | null; windKmh: number | null; elevationM: number; tz: string } | null;
}
export interface TeamProfile {
team: string;
elo: number;