Lineup pitch with live substitution tracking + polish pass
- LineupPitch: both XIs on a vertical pitch at FotMob's per-player formation coordinates — shirt numbers, live ratings, goal badges, formation + coach labels. Substitutes take over the outgoing player's slot (paired through ESPN's 'X replaces Y' commentary with a unique same-minute fallback — the sources romanize names differently), with a subbed-off strip below; the page's live refetch keeps it current through the match. Falls back to the list view (and hides predicted pre-kickoff XIs) when no confirmed lineup is captured. - /api/match-rich now serves the trimmed FotMob lineup (only complete, fully-positioned XIs) - Fix: vs-Market label box was too narrow — the probability bar covered the end of 'DraftKings' - Timeline spine no longer runs past the kick-off/full-time pills Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import { XgRace } from '@/components/XgRace';
|
||||
import { ShotMap } from '@/components/ShotMap';
|
||||
import { MomentumStrip } from '@/components/MomentumStrip';
|
||||
import { EventIcon, MatchTimeline } from '@/components/MatchTimeline';
|
||||
import { LineupPitch } from '@/components/LineupPitch';
|
||||
import { teamFlag } from '@/lib/teams';
|
||||
import { fmt, useFormat, useT } from '@/lib/i18n';
|
||||
import { goalEvents } from '@/lib/matchEvents';
|
||||
@@ -418,16 +419,28 @@ export function MatchPreviewPage() {
|
||||
{/* ── Lineups ── */}
|
||||
{tab === 'lineups' && (
|
||||
<div className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader className="flex items-center gap-2"><Shirt size={16} className="text-accent" /><span className="font-display font-bold text-ink">{preview.lineups ? t.match.lineups : t.match.keyPlayers}</span></CardHeader>
|
||||
<CardBody className="grid grid-cols-2 gap-6">
|
||||
{preview.lineups ? (
|
||||
<><LineupCol team={homeName} players={preview.lineups.home} /><LineupCol team={awayName} players={preview.lineups.away} /></>
|
||||
) : (
|
||||
<><ScorerList team={homeName} players={preview.keyPlayers.home} /><ScorerList team={awayName} players={preview.keyPlayers.away} /></>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
{/* FotMob serves predicted XIs before kickoff — only draw the pitch
|
||||
once the match has started or ESPN confirms the lineups. */}
|
||||
{rich?.lineup && (hasScore || preview.lineups) ? (
|
||||
<Card>
|
||||
<CardHeader className="flex items-center gap-2"><Shirt size={16} className="text-accent" /><span className="font-display font-bold text-ink">{t.match.lineups}</span></CardHeader>
|
||||
<CardBody>
|
||||
<LineupPitch lineup={rich.lineup} home={homeName} away={awayName} events={preview.events} />
|
||||
<p className="mt-2 text-xs text-faint">{t.match.xgAttribution}</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
) : (
|
||||
<Card>
|
||||
<CardHeader className="flex items-center gap-2"><Shirt size={16} className="text-accent" /><span className="font-display font-bold text-ink">{preview.lineups ? t.match.lineups : t.match.keyPlayers}</span></CardHeader>
|
||||
<CardBody className="grid grid-cols-2 gap-6">
|
||||
{preview.lineups ? (
|
||||
<><LineupCol team={homeName} players={preview.lineups.home} /><LineupCol team={awayName} players={preview.lineups.away} /></>
|
||||
) : (
|
||||
<><ScorerList team={homeName} players={preview.keyPlayers.home} /><ScorerList team={awayName} players={preview.keyPlayers.away} /></>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
)}
|
||||
<div className="grid items-start gap-4 md:grid-cols-2">
|
||||
{(preview.form.home.length > 0 || preview.form.away.length > 0) && (
|
||||
<Card>
|
||||
|
||||
Reference in New Issue
Block a user