Skip to main content

Palmarès

Cette page couvre le palmarès CEL au sens large: récompenses de saison, honneurs joueurs, TOTS, MVP/Ballon d’Or, TOTW et MVP de match. Elle documente l’état actuellement implémenté dans le code.

Sources principales

  • convex/schema.ts
  • convex/competition/season_awards.ts
  • convex/lib/season_mvp.ts
  • convex/competition/totw.ts
  • convex/awards/recipients.ts
  • web/src/app/admin/season-awards
  • web/src/app/(app)/palmares/[awardId]/page.tsx
  • web/src/components/club/ClubPalmaresTab.tsx
  • web/src/components/player/PlayerHonoursPanel.tsx

Modèle de publication

Le palmarès de fin de saison est publié depuis l’admin season-awards. La publication crée:
  • une ligne season_awards, qui porte le snapshot global publié;
  • des lignes season_award_entries, qui portent les entrées visibles du palmarès de saison;
  • des lignes award_recipients, qui normalisent les honneurs par joueur.
award_recipients sert de table commune pour les honneurs publics joueur:
TypeSource
season_championpalmarès de saison
season_runner_uppalmarès de saison
season_mvppalmarès de saison
best_gkpalmarès de saison
totspalmarès de saison
totwéquipe de la semaine
motmmatch

Catégories de saison

Les catégories de season_award_entries sont:
CatégorieSens
CHAMPIONclub champion de la saison
RUNNER_UPclub vice-champion
SEASON_MVPMVP/Ballon d’Or de la saison
BEST_GKmeilleur gardien
TOTSonze de saison
La page publique /palmares/[awardId] expose les trophées de saison et la TOTS publiée. Les onglets de club et de joueur réutilisent les entrées normalisées pour afficher les honneurs dans les profils.

Sortie publique /palmares/[awardId] (contrat)

La query publique getPublishedSeasonAwards retourne désormais:
  • finalists.mvp: les 5 meilleurs nommés MVP depuis season_awards.mvpNomineesSnapshot;
  • finalists.bestGk: les 5 meilleurs nommés meilleur gardien depuis season_awards.bestGkNomineesSnapshot;
Chaque entrée de finalists contient au minimum:
  • playerProfileId, name, avatarUrl, clubName, clubLogoUrl, position, rank, goals, assists, matchesPlayed, score (les champs peuvent être null si le snapshot source est incomplet).
La section tots.players expose aussi une sous-structure stats limitée aux champs:
  • goals, assists, matchesPlayed, performanceScore, ratingAverage, totwCount, matchMvpCount, teamRank.
Pour les catégories SEASON_MVP et BEST_GK, le snapshot persisté par publishSeasonAwards inclut aussi ratingAverage dans entry.stats; la valeur peut être null si elle n’est pas calculable. Pour BEST_GK, entry.stats inclut aussi:
  • saves,
  • cleanSheets.

Règles de publication

Règles vérifiées dans publishSeasonAwards:
  • publication réservée aux admins;
  • ligue requise en statut COMPLETED ou ARCHIVED;
  • sélection MVP requise;
  • sélection meilleur gardien requise;
  • sélection TOTS optionnelle si aucune formation complète n’est disponible;
  • remplacement d’un palmarès existant possible;
  • notifications et emails activés par défaut.

Réactions B4 sur les palmarès

La query publique getAwardCongratulations est disponible côté social:
  • getAwardCongratulations({ awardId, actorUserId? })
    • Retourne { byEntryId: Record<entryId, { count, byReaction, mine }> } pour toutes les entrées de l’award.
    • count: total des réactions de l’entrée.
    • byReaction: compte par réaction (MERITE, QUELLE_SAISON, FIER, RENDEZVOUS).
    • mine: réaction de l’utilisateur courant ou null.
La même surface expose les mutations:
  • congratulate({ awardId, entryId, reaction, actorUserId? })
    • Ajoute/remplace la réaction d’un utilisateur connecté pour une entrée.
  • uncongratulate({ awardId, entryId, actorUserId? })
    • Supprime la réaction de l’utilisateur connecté si elle existe.
Spécification B4:
  • une seule réaction active par (entryId, utilisateur).
  • pas d’usage de collect().length pour les compteurs; lecture via table dénormalisée award_congratulation_counts.

Statut personnel du palmarès (B5)

La query getMyPalmaresStatus({ awardId, actorUserId? }) retourne, pour l’utilisateur connecté:
  • awardedEntries: les entryId publics du palmarès où l’utilisateur est lauréat;
  • totsSlots: les slots TOTS publics associés à cet utilisateur;
  • isAwarded: true dès qu’au moins une entrée du palmarès lui correspond.
La résolution croise:
  • season_award_entries.recipientUserId, utile pour les entrées joueur et les destinataires directs;
  • award_recipients, utile pour les récompenses de club qui sont normalisées vers les joueurs actifs au moment de la publication.

Requêtes de sélection de saison

La couche publique expose désormais deux queries pour trouver des publications:
  • listPublishedSeasonAwards({ leagueId? }){ awardId, leagueId, leagueName, season, publishedAt }[], triées en publishedAt décroissant.
  • getLatestPublishedSeasonAwards({ leagueId? }) → le même format pour la publication la plus récente, ou null si aucune.
Quand leagueId est fourni, ces queries restreignent à une ligue précise.

Modèle de comparaison par poste

TOTS et MVP partagent le même principe: un joueur n’est jamais comparé à toute la ligue en vrac, mais aux autres joueurs de son poste. La chaîne de résolution du poste se fait en trois temps.

1. Normalisation du poste détaillé

Les libellés bruts sont d’abord ramenés à un code détaillé canonique (normalizePositionDetailedForStats). Exemples d’alias:
Codes brutsCode canonique
GK, G, GOALKEEPERGK
CB, DEF, DEFENDERDC
LB, LWBDG
RB, RWBDD
CDMMDC
CM, MIDMC
CAMMOC
LM / LWMG / AG
RM / RWMD / AD
ST, CF, ATT, FORWARDBU
On obtient ~13 postes détaillés: GK, DG, DC, DD, MDC, MC, MOC, MG, MD, AG, AD, BU.

2. Famille de comparaison

Après normalisation, le poste détaillé est rattaché à une famille de scoring (resolveScorePositionFamily). C’est cette famille qui sert au calcul des barèmes de performance.
FamillePostes détaillés
GKGK
DCDC
FULLBACKDG, DD
MDCMDC
MIDMC, MOC
WIDEMG, MD, AG, AD
BUBU
Conséquence: un DG et un DD partagent le benchmark FULLBACK; un MG, un MD, un AG et un AD partagent le benchmark WIDE. Les barèmes de performance (meilleur total, moyenne des 10 meilleurs) sont calculés par famille de poste, pas par poste détaillé isolé. Le poste détaillé reste conservé pour l’affichage, l’éligibilité aux cases de formation et la sélection du slot TOTS. Il évite par exemple de traiter un MD comme un latéral DD, même si les deux jouent dans un couloir.

3. Agrégation multi-postes d’un même joueur

Un joueur joué à plusieurs libellés qui retombent sur la même famille de comparaison voit ses lignes fusionnées (aggregateTotsPositionStats): matchs dédoublonnés par matchId, buts/passes/points cumulés. Le poste représentatif est celui où il a le plus de matchs.

Formule TOTS (tots-v3)

La TOTS désigne les meilleurs joueurs à chaque poste. La comparaison statistique se fait au sein de la famille de poste du joueur. Pour être candidat à un poste, un joueur doit franchir deux seuils:
  • un seuil de saison: avoir joué au moins min(10, matchs max de la saison) matchs au total;
  • un seuil de poste: avoir joué au moins max(15, plafond(20% de ses matchs de saison)) matchs dans la famille de comparaison considérée.
Le 15 n’est donc qu’un plancher: un joueur qui a disputé 100 matchs doit en avoir joué au moins 20 au poste pour y être candidat. La régularité ne doit pas ajouter de points séparés: elle est portée par ces seuils d’éligibilité.

Performance /70

La performance utilise le total de points cumulés dans la famille, pas une moyenne par match.
Performance = (total points du joueur dans la famille / meilleur total de la famille) x 70
Exemple:
  • meilleur total de la famille: 500 points;
  • total du joueur: 450 points;
  • performance: 450 / 500 x 70 = 63.

Distinctions /10

Distinctions = min(TOTW x 2, 6) + min(MVP match x 2, 4)

Collectif /20

ClassementPoints
1er20
2e18
3e16
4e14
5e12
6e10
7e8
8e6
9e4
10e à 14e2

Total TOTS

Score TOTS = performance /70 + distinctions /10 + collectif /20

Formule MVP / Ballon d’Or (mvp-v2)

Le MVP/Ballon d’Or doit comparer toute la ligue sans favoriser automatiquement les postes offensifs. Il utilise donc un Performance Index basé sur la famille de poste.

Famille retenue et éligibilité (changement v3)

Avant la v3, le MVP notait chaque joueur sur ses totaux globaux de saison (tous postes confondus) et son poste principal. La v3 change cela (selectMvpScoringPosition + toSeasonMvpCandidate):
  • on regroupe les stats du joueur par famille de comparaison (même chaîne que la TOTS), on agrège, puis on ne garde que les postes franchissant le seuil d’éligibilité max(15, plafond(20% des matchs de saison));
  • parmi ces postes éligibles, on retient le meilleur (plus haut total de points, puis plus de matchs);
  • les matchs, buts, passes et points du candidat MVP proviennent de ce seul poste, pas de ses totaux globaux;
  • un joueur qui ne franchit le seuil à aucun poste est exclu du classement.
Ensuite, le Performance Index compare le total du joueur aux barèmes de sa famille de poste (meilleur total + moyenne des 10 meilleurs de cette famille). C’est ce qui neutralise l’avantage des postes offensifs: un défenseur excellent est noté par rapport aux meilleurs défenseurs, pas par rapport aux buteurs. Pour le meilleur gardien, le même mécanisme s’applique en forçant le poste GK.

Performance Index /70

A = total points du joueur au poste / meilleur total du poste
B = total points du joueur au poste / moyenne des 10 meilleurs totaux du poste

Performance Index = 0,7 x A + 0,3 x B
Performance MVP = Performance Index x 70
Exemple:
  • meilleur total du poste: 500;
  • moyenne des 10 meilleurs du poste: 476;
  • total du joueur: 500;
  • A = 500 / 500 = 1;
  • B = 500 / 476 = 1,05;
  • Performance Index = 0,7 x 1 + 0,3 x 1,05 = 1,015.
La performance MVP doit rester plafonnée à 70 points pour conserver un total final sur 100.

Distinctions /10

Le MVP utilise le même système de distinctions que la TOTS:
Distinctions = min(TOTW x 2, 6) + min(MVP match x 2, 4)
Les bonus Top 5 buteur, passeur ou gardien ne font pas partie de la formule.

Collectif /20

Le MVP utilise le même bonus collectif que la TOTS.

Total MVP

Score MVP = performance index /70 + distinctions /10 + collectif /20

Versioning

  • TOTS: tots-v3;
  • MVP/Ballon d’Or: mvp-v2.
Les versions de formule sont stockées dans les snapshots publiés afin de préserver les palmarès déjà générés.
Last modified on June 25, 2026