Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/IMPLEMENTATION_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ Working:
- per-match prediction locking based on each match's kickoff time
- tournament leaderboard
- spectator standings and knockout progress
- live group standings table derived from finished match scores
(P / W / D / L / GF / GA / GD / Pts), with an optional toggle to view
the same table populated by the current user's predicted ordering

### 2.4 League Participation

Expand Down
10 changes: 10 additions & 0 deletions docs/QA_CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,22 @@ Expected:
5. Continue through knockout rounds.
6. Save.
7. Return to the tournament page.
8. Switch between the `Groups`, `Standings`, and `Knockout` tabs.
9. In the `Standings` tab, toggle between `Live results` and `My picks`.

Expected:

- the tournament page recognizes saved predictions
- user can return to the prediction wizard before lock
- predictions persist on reload
- before any matches are played, the live standings show all zeros
(matching the FIFA standings page baseline)
- once group-stage scores are imported, each group's standings reflect
the running P / W / D / L / GF / GA / GD / Pts derived from finished
matches, sorted by points then goal difference then goals-for
- the `My picks` view shows the user's predicted 1st / 2nd / 3rd / 4th
ordering with stats columns dashed out (predictions store ordering
only, not score predictions)

## 9. Random Fill

Expand Down
25 changes: 25 additions & 0 deletions src/i18n/messages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,32 @@ export const EN = {
groups: 'Groups',
groupStandings: 'Group standings',
standings: 'Standings',
knockout: 'Knockout',
knockoutBracket: 'Knockout progress',
standingsViewActual: 'Live results',
standingsViewPicks: 'My picks',
standingsPicksNote: 'Predictions only store finishing order, so stats columns stay empty here.',
standingsEmpty: 'No group-stage matches have been played yet.',
standingsTable: {
rank: '#',
team: 'Team',
played: 'P',
won: 'W',
drawn: 'D',
lost: 'L',
goalsFor: 'GF',
goalsAgainst: 'GA',
goalDiff: 'GD',
points: 'Pts',
playedFull: 'Played',
wonFull: 'Won',
drawnFull: 'Drawn',
lostFull: 'Lost',
goalsForFull: 'Goals for',
goalsAgainstFull: 'Goals against',
goalDiffFull: 'Goal difference',
pointsFull: 'Points',
},
winner: 'Winner',
finalTime: 'FT',
makeYourPredictions: 'Make Your Predictions',
Expand Down
25 changes: 25 additions & 0 deletions src/i18n/messages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,32 @@ export const ES = {
groups: 'Grupos',
groupStandings: 'Posiciones de grupos',
standings: 'Posiciones',
knockout: 'Eliminatorias',
knockoutBracket: 'Progreso eliminatorio',
standingsViewActual: 'Resultados reales',
standingsViewPicks: 'Mis predicciones',
standingsPicksNote: 'Las predicciones solo guardan el orden final, por eso las columnas de estadísticas quedan vacías aquí.',
standingsEmpty: 'Todavía no se jugaron partidos de la fase de grupos.',
standingsTable: {
rank: '#',
team: 'Equipo',
played: 'PJ',
won: 'G',
drawn: 'E',
lost: 'P',
goalsFor: 'GF',
goalsAgainst: 'GC',
goalDiff: 'DG',
points: 'Pts',
playedFull: 'Partidos jugados',
wonFull: 'Victorias',
drawnFull: 'Empates',
lostFull: 'Derrotas',
goalsForFull: 'Goles a favor',
goalsAgainstFull: 'Goles en contra',
goalDiffFull: 'Diferencia de goles',
pointsFull: 'Puntos',
},
winner: 'Ganador',
finalTime: 'FT',
makeYourPredictions: 'Haz Tus Predicciones',
Expand Down
25 changes: 25 additions & 0 deletions src/i18n/messages/it.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,32 @@ export const IT = {
groups: 'Gironi',
groupStandings: 'Classifiche gironi',
standings: 'Classifica',
knockout: 'Eliminatorie',
knockoutBracket: 'Andamento eliminatorie',
standingsViewActual: 'Risultati reali',
standingsViewPicks: 'I miei pronostici',
standingsPicksNote: 'I pronostici memorizzano solo l\'ordine finale, quindi qui le colonne statistiche restano vuote.',
standingsEmpty: 'Non si sono ancora giocate partite della fase a gironi.',
standingsTable: {
rank: '#',
team: 'Squadra',
played: 'PG',
won: 'V',
drawn: 'N',
lost: 'P',
goalsFor: 'GF',
goalsAgainst: 'GS',
goalDiff: 'DR',
points: 'Pt',
playedFull: 'Partite giocate',
wonFull: 'Vittorie',
drawnFull: 'Pareggi',
lostFull: 'Sconfitte',
goalsForFull: 'Gol fatti',
goalsAgainstFull: 'Gol subiti',
goalDiffFull: 'Differenza reti',
pointsFull: 'Punti',
},
winner: 'Vincitore',
finalTime: 'FT',
makeYourPredictions: 'Inserisci i pronostici',
Expand Down
25 changes: 25 additions & 0 deletions src/i18n/messages/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,32 @@ export const NL = {
groups: 'Groepen',
groupStandings: 'Groepsstanden',
standings: 'Stand',
knockout: 'Knock-out',
knockoutBracket: 'Voortgang knock-out',
standingsViewActual: 'Echte resultaten',
standingsViewPicks: 'Mijn voorspellingen',
standingsPicksNote: 'Voorspellingen bewaren alleen de eindvolgorde, dus de statistiekkolommen blijven hier leeg.',
standingsEmpty: 'Er zijn nog geen groepsfasewedstrijden gespeeld.',
standingsTable: {
rank: '#',
team: 'Team',
played: 'GS',
won: 'W',
drawn: 'G',
lost: 'V',
goalsFor: 'DV',
goalsAgainst: 'DT',
goalDiff: 'DS',
points: 'Ptn',
playedFull: 'Gespeeld',
wonFull: 'Gewonnen',
drawnFull: 'Gelijk',
lostFull: 'Verloren',
goalsForFull: 'Doelpunten voor',
goalsAgainstFull: 'Doelpunten tegen',
goalDiffFull: 'Doelsaldo',
pointsFull: 'Punten',
},
winner: 'Winnaar',
finalTime: 'FT',
makeYourPredictions: 'Doe je voorspellingen',
Expand Down
25 changes: 25 additions & 0 deletions src/i18n/messages/pt.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,32 @@ export const PT = {
groups: 'Grupos',
groupStandings: 'Classificação dos grupos',
standings: 'Classificação',
knockout: 'Mata-mata',
knockoutBracket: 'Andamento do mata-mata',
standingsViewActual: 'Resultados reais',
standingsViewPicks: 'Meus palpites',
standingsPicksNote: 'Os palpites guardam apenas a ordem final, por isso as colunas de estatística ficam vazias aqui.',
standingsEmpty: 'Ainda não foram disputados jogos da fase de grupos.',
standingsTable: {
rank: '#',
team: 'Equipa',
played: 'J',
won: 'V',
drawn: 'E',
lost: 'D',
goalsFor: 'GM',
goalsAgainst: 'GS',
goalDiff: 'SG',
points: 'Pts',
playedFull: 'Jogos disputados',
wonFull: 'Vitórias',
drawnFull: 'Empates',
lostFull: 'Derrotas',
goalsForFull: 'Golos marcados',
goalsAgainstFull: 'Golos sofridos',
goalDiffFull: 'Saldo de golos',
pointsFull: 'Pontos',
},
winner: 'Campeão',
finalTime: 'FT',
makeYourPredictions: 'Faça seus palpites',
Expand Down
175 changes: 174 additions & 1 deletion src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -1814,11 +1814,182 @@ html.theme-light .nav-dropdown-settings,
.tournament-league-grid,
.tournament-groups-grid,
.tournament-knockout-grid,
.tournament-standings-grid,
.tournament-owned-leagues {
display: grid;
gap: 1rem;
}

.tournament-view-tabs {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
padding: 0.35rem;
border-radius: 1.25rem;
border: 1px solid var(--line-soft);
background: rgba(7, 17, 31, 0.6);
}

.tournament-view-tabs__tab {
appearance: none;
flex: 1 1 auto;
min-width: 6.5rem;
border: 1px solid transparent;
border-radius: 0.95rem;
padding: 0.55rem 1.05rem;
background: transparent;
color: var(--text-muted);
font-weight: 700;
font-size: 0.95rem;
letter-spacing: 0.01em;
cursor: pointer;
transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.tournament-view-tabs__tab:hover {
color: var(--text-strong);
}

.tournament-view-tabs__tab.is-active {
background: rgba(13, 165, 103, 0.18);
border-color: rgba(13, 165, 103, 0.55);
color: var(--text-strong);
}

.tournament-standings-toggle {
display: inline-flex;
gap: 0.4rem;
padding: 0.3rem;
border-radius: 0.95rem;
border: 1px solid var(--line-soft);
background: rgba(7, 17, 31, 0.55);
}

.tournament-standings-toggle__btn {
appearance: none;
border: 1px solid transparent;
border-radius: 0.7rem;
padding: 0.4rem 0.85rem;
background: transparent;
color: var(--text-muted);
font-weight: 700;
font-size: 0.85rem;
cursor: pointer;
transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.tournament-standings-toggle__btn:hover {
color: var(--text-strong);
}

.tournament-standings-toggle__btn.is-active {
background: rgba(13, 165, 103, 0.2);
border-color: rgba(13, 165, 103, 0.55);
color: var(--text-strong);
}

.tournament-standings-note {
color: var(--text-muted);
font-size: 0.9rem;
margin: 0;
}

.tournament-standings-card {
display: grid;
gap: 0.85rem;
min-height: 100%;
align-content: start;
}

.tournament-standings-card__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}

.tournament-standings-table-wrap {
overflow-x: auto;
}

.tournament-standings-table {
width: 100%;
border-collapse: collapse;
font-size: 0.88rem;
color: var(--text-strong);
}

.tournament-standings-table th,
.tournament-standings-table td {
padding: 0.55rem 0.4rem;
text-align: center;
white-space: nowrap;
}

.tournament-standings-table thead th {
color: var(--text-muted);
font-weight: 700;
font-size: 0.78rem;
letter-spacing: 0.05em;
text-transform: uppercase;
border-bottom: 1px solid var(--line-soft);
}

.tournament-standings-table tbody tr + tr td {
border-top: 1px solid rgba(255, 255, 255, 0.05);
}

.tournament-standings-table__rank-col {
width: 2.2rem;
text-align: center;
color: var(--text-muted);
font-weight: 700;
}

.tournament-standings-table__team-col {
text-align: left;
min-width: 8.5rem;
}

.tournament-standings-table__team-name {
display: inline-block;
font-weight: 600;
}

.tournament-standings-table__team-code {
display: inline-block;
margin-inline-start: 0.45rem;
color: var(--text-muted);
font-size: 0.78rem;
letter-spacing: 0.04em;
}

.tournament-standings-table__points-col {
font-weight: 800;
color: var(--text-strong);
}

:root[data-theme='light'] .tournament-view-tabs,
:root[data-theme='light'] .tournament-standings-toggle {
background: rgba(255, 255, 255, 0.85);
border-color: rgba(16, 32, 51, 0.12);
}

:root[data-theme='light'] .tournament-view-tabs__tab,
:root[data-theme='light'] .tournament-standings-toggle__btn {
color: #4f6b83;
}

:root[data-theme='light'] .tournament-view-tabs__tab.is-active,
:root[data-theme='light'] .tournament-standings-toggle__btn.is-active {
background: rgba(13, 165, 103, 0.16);
color: #0c2236;
}

:root[data-theme='light'] .tournament-standings-table tbody tr + tr td {
border-top-color: rgba(16, 32, 51, 0.08);
}

.tournament-league-card {
min-height: 100%;
padding: 1.25rem;
Expand Down Expand Up @@ -2210,6 +2381,7 @@ html.theme-light .nav-dropdown-settings,

.tournament-groups-grid,
.tournament-knockout-grid,
.tournament-standings-grid,
.tournament-owned-leagues {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
Expand Down Expand Up @@ -2322,7 +2494,8 @@ html.theme-light .nav-dropdown-settings,
grid-template-columns: repeat(4, minmax(0, 1fr));
}

.tournament-groups-grid {
.tournament-groups-grid,
.tournament-standings-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}

Expand Down
Loading
Loading