> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cel-eleague.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Modele donnees

# Modèle de données Convex (constats vérifiés)

Source technique principale : `convex/schema.ts`, complétée par `convex/_generated/dataModel.d.ts` pour les tables Auth.

Repères:

* `id` est l’identifiant métier/partagé.
* `_id` est l’identifiant interne Convex.
* Ce document ne liste que les entités demandées explicitement et les invariants de schéma vérifiés.

## Authentification

### `users`

* Champs clés: `id`, `role`, `accountStatus`, `accountStatusUpdatedAt`, `selfDeletedAt`, `username`, `email`, `userProfiles?`, `searchText`.
* Enum vérifiés:
  * `users.role` -> `USER`, `ADMIN`, `MODERATOR`
  * `users.accountStatus` -> `ACTIVE`, `DEACTIVATED`, `DELETED`
* Index vérifiés: `by_accountStatus`, `by_role`, `by_external_id`.

### `authTables` + `authVerifiers`

* `authTables` provient de `@convex-dev/auth/server` dans le schéma (`...authTables`).
* `authAccounts`: `provider`, `providerAccountId`, `secret?`, `userId`, `emailVerified?`, `phoneVerified?`.
  * Index vérifiés: `providerAndAccountId`, `userIdAndProvider`.
* `authSessions`: `userId`, `expirationTime`.
  * Index vérifié: `userId`.
* `authRefreshTokens`: `expirationTime`, `firstUsedTime?`, `parentRefreshTokenId?`, `sessionId`.
  * Index vérifiés: `sessionId`, `sessionIdAndParentRefreshTokenId`.
* `authVerificationCodes`: `accountId`, `code`, `provider`, `verifier?`, `expirationTime`, `emailVerified?`, `phoneVerified?`.
  * Index vérifiés: `accountId`, `code`.
* `authRateLimits`: table générée en plus d’`authTables` (champ `attemptsLeft` confirmé).
* `authVerifiers` (défini dans `schema.ts`) :
  * `sessionId?`, `signature?`.
  * Index: `sessionId`, `signature`.

## Matchs et planification

### `match_reports`

* Champs vérifiés: `matchId`, `leagueId`, `reportingClubId`, `reportedByUserId`, `scheduledAtBefore`, `scheduledAtAfter`, `counterProposedDate?`, `respondedAt?`, `respondedById?`, `status?`, `createdAt`.
* Références: `match`, `league`, `reportingClub`, `reportedBy`, `respondedBy`.
* Enum de statut (vérifié): `PENDING`, `ACCEPTED`, `COUNTER_PROPOSED`, `CANCELLED`, `REJECTED`.
* Index vérifiés:
  * `by_matchId`, `by_matchId_status`, `by_leagueId_reportingClubId`, `by_reportingClubId_createdAt`, `by_external_id`, `by_league`.

### `match_lineups`

* Champs vérifiés: `matchId`, `clubId`, `formation`, `assignments`, `status`, `createdAt`, `updatedAt`, `submittedAt?`, `submittedById?`.
* `assignments` contient `slotKey`, `playerProfileId`, `positionDetailed`.
* Enum de `positionDetailed` vérifiée: `GK`, `DG`, `DC`, `DD`, `MDC`, `MC`, `MOC`, `MG`, `MD`, `AG`, `AD`, `BU`.
* Enum de `status` vérifiée: `draft`, `submitted`.
* Index vérifiés:
  * `by_matchId_clubId`, `by_status_submittedAt`, `by_clubId_status_updatedAt`, `by_clubId_updatedAt`, `by_external_id`.

## Rôles de club et historique

### `club_members`

* Enum de rôle vérifié: `MANAGER`, `CO_MANAGER`, `COACH`, `MEMBER`.

### `role_changes`

* Champs vérifiés: `clubMemberId`, `type`, `fromRole`, `toRole`, `initiatedById`, `reason?`, `createdAt`, `clubMember`, `initiatedBy`.
* Enum vérifiés:
  * `type`: `PROMOTION`, `DEMOTION`
  * `fromRole` / `toRole`: `MANAGER`, `CO_MANAGER`, `COACH`, `MEMBER`.
* Index vérifiés: `by_clubMemberId`, `by_createdAt`, `by_external_id`.

## Awards / TOTS / TOTW

### `season_awards`, `season_award_previews`, `season_award_entries`

* `season_awards.status` est un champ statut figé en `PUBLISHED` (publication des récompenses).
* Champs structurants: `leagueId`, `season`, `publishedAt`, `status`, `publishedByUserId`, `emailStatus`, `coverage`, snapshots formule.
* `season_awards` conserve `warningCount` et compteur de destinataires.
* `season_award_previews` stocke: `leagueId`, `season`, `preview`, `generatedAt`, `generatedByUserId`.
* `season_award_entries.category`: `CHAMPION`, `RUNNER_UP`, `SEASON_MVP`, `BEST_GK`, `TOTS`.
* `season_award_entries.recipientType`: `PLAYER`, `CLUB`.

### `award_recipients` et TOTW

* `awardType` vérifiés: `season_champion`, `season_runner_up`, `season_mvp`, `best_gk`, `tots`, `totw`, `motm`.
* `sourceType` vérifié: `season_award`, `team_of_the_week`, `match`.

### `award_congratulations`

* Champs clés:
  * `id`: identifiant métier/partagé (compat ID),
  * `seasonAwardId`: identifiant métier du `season_awards`,
  * `entryId`: identifiant métier du `season_award_entry` concerné,
  * `congratulatorUserId`: identifiant métier de l’utilisateur qui réagit,
  * `reaction`: enum `MERITE`, `QUELLE_SAISON`, `FIER`, `RENDEZVOUS`,
  * `createdAt`: timestamp ms,
  * `leagueId`: identifiant métier de la ligue,
  * `seasonAward`: référence `_id` vers `season_awards`,
  * `seasonAwardEntry`: référence `_id` vers `season_award_entries`,
  * `user`: référence `_id` vers `users`,
  * `league`: référence `_id` vers `leagues`.
* Contraintes observées par index:
  * `by_external_id` sur `id`,
  * `by_entry` sur `entryId`,
  * `by_entry_user` sur `entryId + congratulatorUserId`.

### `award_congratulation_counts`

* Champs clés:
  * `id`: identifiant métier/partagé (compat ID),
  * `entryId`: identifiant métier de `season_award_entry`,
  * `count`: total des réactions sur l’entrée,
  * `byReaction`: objet de compte par réaction avec clés `MERITE`, `QUELLE_SAISON`, `FIER`, `RENDEZVOUS`,
  * `seasonAwardId`: identifiant métier du `season_awards`,
  * `entry`: référence `_id` vers `season_award_entries`,
  * `seasonAward`: référence `_id` vers `season_awards`,
  * `league`: référence `_id` vers `leagues`.

* Contraintes observées par index:
  * `by_external_id` sur `id`,
  * `by_entry` sur `entryId`.

* Usage B4:
  * `award_congratulations` stocke la réaction active par utilisateur.
  * `award_congratulation_counts` dénormalise les totaux pour éviter les `collect().length`.

### `team_of_the_week`, `team_of_the_week_players`

* `team_of_the_week.calcVersion` vérifié: `totw-v1`, `totw-v2`, `totw-v3`.
* Champs structurants: `leagueId`, `season`, `matchdayFrom`, `matchdayTo`, `periodStartAt`, `periodEndAt`, `selectionKey`, `formation`, `publishedAt`.
* `team_of_the_week_players.calcVersion` vérifié: `totw-v2`, `totw-v3`.
* `team_of_the_week_players.fitKind` vérifié: `exact`, `compatible`, `fallback`.
* Champs vérifiés côté joueurs: `position`, `positionDetailed?`, `selectionSlot`, `selectionRole`, `selectionPositionDetailed`, `candidateKey`, scores bruts.

## Discipline et restrictions (table `sanctions`)

* `targetType` vérifié: `PERSON`, `CLUB` (pas uniquement personne).
* `sanctionType` vérifiés (liste observée dans le schéma): `BAN_PLAYER`, `SUSPENSION`, `BAN_CLUB`, `PENDING_DISCIPLINARY`, `POINTS_DEDUCTION`, `FINE`, `FORFAIT`, `PERSON_BAN`, `PERSON_SUSPENSION`, `PERSON_DISCIPLINARY_REVIEW`, `COMPETITION_BAN`.
* `effectScopes` vérifiés: `APP_ACCESS`, `SPORT_ELIGIBILITY`, `VISIBILITY`, `CONTACT`.
* `lifecycleState` vérifiés: `ACTIVE`, `ENDED_MANUAL`, `ENDED_EXPIRED`, `ENDED_REPLACED`.
* `endSource` vérifiés: `MANUAL`, `AUTO_EXPIRED`, `REPLACED`.

## Zone non vérifiée / à confirmer

* Les règles métier complètes d’envoi notification (priorités, cadence de retry, templates) ne sont pas détaillées dans ce modèle de données.
* Les comportements métier de certains types de sanction (par ex. interaction exacte entre `banScope` et l’admissibilité en compétition) sont documentés côté règles métiers, pas côté modèle.
