fix(predict): world cup fixes cp-7.80.0#30878
Conversation
Live World Cup soccer markets were being filtered out of the Live tab while matches were still ongoing. Root cause: live game markets carry recurrence=DAILY and a market-level endDate set to the *scheduled* match end. getVisiblePredictMarket ran the time-based expiry check before the game-market check, and isPredictMarketExpiredByTime fell through to the daily endDate<=now heuristic for any non-ended game. Matches routinely run past their scheduled endDate (stoppage/extra time, penalties), so ongoing games were wrongly treated as expired and hidden. Fixes: - isPredictMarketExpiredByTime is now authoritative for game markets: a game expires only when the game itself is over (status 'ended' or an endTime is stamped), never via the scheduled endDate. - Drop series_id from the live query; tag_slug + live=true is enough, so the Live UI can be repointed (e.g. to all soccer) via feature flag. - Remove debugging console logs. PRED-942
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
The live elapsed time was centered against the dot + "Live" group, leaving it offset from the "Live" label. Per Figma, the pulsing dot now sits to the left of the centered "Live" text (absolutely positioned so it no longer affects layout), so "Live" and the elapsed time share the same center axis. PRED-942
The absolute-positioned dot anchored to the full-width column, drifting toward the score on the left. Replace it with an in-flow dot plus an invisible spacer of equal width on the right, so the dot stays adjacent to "Live" while the label and elapsed time remain centered on the same axis. PRED-942
Adds period/elapsed-aware live status text and extracts the card scoreboard
into the shared PredictSportScoreboard component (replacing the legacy
details-screen UI) so cards and the game details screen render the same
scoreboard.
Live status text now follows three formats:
- "{period}" at a breaking/terminal period (e.g. Halftime, Final)
- "{elapsed}’" for soccer during running play (e.g. 75’)
- "{period} • {elapsed}" for other sports during running play (e.g. Q1 • 8:15)
- Add getSportLiveStatusText/isBreakingPeriod helpers (utils/scoreboard.ts)
- Add isSoccerLeague (constants/sports.ts)
- Rewrite PredictSportScoreboard to the card scoreboard UI (logos, scores,
live/scheduled/final center, team names); supports a compact variant
- Refactor PredictMarketSportCard to render PredictSportScoreboard
PRED-942
The live query now uses tag_slug + live=true, so seriesId is no longer read anywhere in the World Cup flow. Remove it entirely from PredictWorldCupConfig, its default flag, schema, and the data-config Pick types so callers no longer supply a dead field. Addresses PR review (Cursor Bugbot) on usePredictWorldCup data config. PRED-942
|
Hey @matallui found these: 🔴 High severityH1. Removing
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #30878 +/- ##
=======================================
Coverage 82.72% 82.72%
=======================================
Files 5566 5568 +2
Lines 143286 143290 +4
Branches 33099 33079 -20
=======================================
+ Hits 118529 118542 +13
+ Misses 16870 16863 -7
+ Partials 7887 7885 -2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Switch PredictWorldCupSchema from strict object() to type() so unknown keys in the remote feature-flag payload are ignored instead of throwing. Remote config is managed independently of client releases. After removing seriesId from the client, a remote payload still containing seriesId would make superstruct's strict object() throw, parse() would fall back to the disabled default, and World Cup would be silently turned off for all users. type() also makes the flag forward-compatible with future remote-only fields. Also removes the now-dead PredictSportWinner component (no longer used after the scoreboard unification). Addresses PR review (H1 schema strictness, M1 dead-code cleanup). PRED-942
|
Thanks for the thorough review @MarioAslau — great catch on H1. H1 (schema strictness → silent disable): fixed in Fix: switched M1 (details scoreboard feature/ordering change): intentional unification. M2 (live query scoping): valid, needs API confirmation. |
Soccer (and other draw-capable leagues) display the home team on the left, while US sports (e.g. American football) follow the away-then-home convention. The scoreboard rewrite had hardcoded home-on-left; restore the original isHomeFirst ordering via isSoccerLeague, keeping testIDs tied to team identity. Possession/winner indicators remain dropped (not part of the new designs). Addresses PR review (M1 team ordering). PRED-942
Restore the flipped prop on the right-side team icon so directional icons (e.g. NFL helmets) mirror to face the left-side team, matching the original scoreboard. The rewrite's renderTeamLogo helper had dropped flipped, leaving both helmets pointing the same way on cards and the game details screen. Addresses PR review (Bugbot: away team icon no longer rendered flipped). PRED-942
PredictMarketSportCard subscribes to live game updates for its buy-button logic; it now passes that gameUpdate down to PredictSportScoreboard instead of the scoreboard opening a second subscription. The scoreboard only self- subscribes when no update prop is provided (e.g. standalone on the details screen), eliminating the duplicate WebSocket connection and 1s polling interval per card. Addresses PR review (Bugbot: duplicate WebSocket subscription for same game). PRED-942
|
Hey @matallui I found this: M2.
|
…ales en.json (the locale source of truth) dropped predict.world_cup.stages .third_place_match, but 14 other locale files still contained it, leaving orphaned keys that diverge from English and can trip locale-consistency tooling. Remove the key from all of them; it is unused in code. Addresses PR review (locale consistency). PRED-942
|
Good catch on the orphaned key @MarioAslau — fixed in Since On the copy tweaks ( |
The scoreboard treats a full-time period ('FT'/'VFT') as ended (renders
"Final"), but the card's buy-button gating only checked status !== 'ended'.
When a provider reports a terminal period before flipping status to 'ended',
the card showed buy buttons on a game that visually reads "Final".
Extract a shared isGameEnded(status, period) helper and use it in both the
scoreboard (Final rendering) and the card (showBuyButtons gating) so they
agree on when a game is over.
Addresses PR review (Bugbot: card buy buttons visible when scoreboard shows Final).
PRED-942
marketStaleness treated a stamped game.endTime as game-over for market visibility, while the scoreboard/card isGameEnded only checked status==='ended' and FT/VFT periods. A provider stamping endTime while status is still 'ongoing' could hide the market while the scoreboard and buy CTAs still showed in-play. Generalize isGameEnded to a single canonical predicate over all terminal signals (status 'ended' OR FT/VFT period OR stamped endTime) and use it in the scoreboard, the card buy-button gating, and marketStaleness, so visibility and UI never disagree. Added unit/regression tests for the endTime and FT cases. Addresses PR review (Bugbot: endTime not ending UI state). PRED-942
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 37ef6ba. Configure here.
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection:
Tag selection rationale:
No changes to core Engine, controllers, navigation infrastructure, or other feature areas. The changes are well-scoped to the Predict feature. Performance Test Selection: |

Description
This PR bundles several fixes and improvements to the Predict World Cup experience and the shared sports scoreboard.
1. Live games no longer disappear from the Live tab
Live World Cup soccer markets were being filtered out of the Live tab while the matches were still ongoing.
Root cause: live game markets carry
recurrence: DAILYand a market-levelendDateset to the match's scheduled end. IngetVisiblePredictMarket, the time-based expiry check ran before the game-market check, and for any non-endedgame it fell through to the dailyendDate <= nowstaleness heuristic. Soccer matches routinely run past their scheduledendDate(stoppage time, halftime, extra time, penalties), so ongoing games were wrongly treated as expired and hidden.Fix:
isPredictMarketExpiredByTimeis now authoritative for game markets — a game market expires only when the game itself is over (status === 'ended'or anendTimeis stamped), never via the scheduledendDate. The dailyendDateheuristic still applies to non-game daily markets.2. Broaden the live query (drop
series_id)The live query now uses
tag_slug+live=true(instead ofseries_id), which is enough to return all live games. This lets us repoint the Live UI (e.g. to all soccer leagues) purely via thetagSlugfeature flag.seriesIdhas been removed entirely fromPredictWorldCupConfig, its default flag, schema, and data-config types since it is no longer read anywhere.3. Scoreboard: live status text + shared component
{period}at a breaking/terminal period (e.g. "Halftime", "Final"){elapsed}’for soccer during running play (e.g. "75’"){period} • {elapsed}for other sports during running play (e.g. "Q1 • 8:15")PredictSportScoreboardcomponent (replacing the legacy details-screen UI) so the card and the game details screen render the same scoreboard. It supports acompactvariant for carousel cards.Changelog
CHANGELOG entry: null
Related issues
Fixes: PRED-942
Manual testing steps
Screenshots/Recordings
Before
After
Screen.Recording.2026-06-01.at.1.35.20.PM.mov
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Medium Risk
Touches market visibility, remote feature-flag parsing, and live betting UI gating; well-tested but affects what users see during live matches and World Cup rollout.
Overview
Fixes Predict World Cup Live and sports UI by aligning game lifecycle, queries, and a shared scoreboard.
Live tab / staleness: Game markets no longer expire from the scheduled market
endDatewhile play is still ongoing (e.g. stoppage/extra time). Expiry uses sharedisGameEnded(status === 'ended',FT/VFT, orendTime), matching scoreboard “Final” and buy-button gating.World Cup queries & flags: Live fetch uses
tag_slug+live=true(dropsseries_id).seriesIdis removed from config/types;PredictWorldCupSchemausestype()so legacy/extra remote keys (e.g.seriesId) don’t fail parsing and disable the feature.Scoreboard: Inline card UI moves to
PredictSportScoreboard(compact, optional parentgameUpdateto avoid duplicate WebSocket). Status text: soccer minutes (75’), other sportsQ • clock, breaks/halftime/final viagetSportLiveStatusText. Sport cards hide buy buttons at full time beforestatusflips to ended. Possession/winner trophy UI removed from scoreboard.Reviewed by Cursor Bugbot for commit 37ef6ba. Bugbot is set up for automated code reviews on this repo. Configure here.