Aller au contenu

Cas d'utilisation — Parties

Terminologie

On utilise le terme "partie" (pas "match"). Format uniquement 2v2 (4 joueurs).


UC-PARTIE-001 · Créer une partie

Acteur : Joueur authentifié
Précondition : Utilisateur connecté

Flux principal

  1. Le joueur accède à « Créer une partie »
  2. Il choisit le type : Amicale ou Compétitive
  3. Il choisit la visibilité : Publique ou Privée
  4. Il choisit l'environnement : Intérieur, Semi-couvert ou Extérieur
  5. Il sélectionne la date et l'heure
  6. Il choisit le lieu : club référencé ou lieu libre (adresse)
  7. Il indique si la piste est déjà réservée (OUI/NON) — ou réserve via l'app si le club le permet
  8. Il sélectionne la durée (60, 90, 120, 150 ou 180 min)
  9. Il définit l'intervalle de niveau accepté (min–max, ex : 3.0 – 5.5)
  10. Il configure le nombre de sets (1, 2 ou 3)
  11. (Optionnel — Pré-remplissage des équipes) Il choisit sa position (équipe + côté) et/ou ajoute des invités (joueurs hors-app) avec leur nom et position
  12. Il valide → la partie est créée, le créateur est inscrit automatiquement (avec la position choisie si pré-remplie)
  13. (Si privée) Il invite des joueurs par lien ou par pseudo/email

UC-PARTIE-002 · Rejoindre une partie publique

Acteur : Joueur authentifié
Précondition : Partie en statut « Ouverte », places disponibles

Flux principal — niveau dans l'intervalle

  1. Le joueur recherche des parties (filtres + géolocalisation)
  2. Il consulte les détails (lieu, heure, joueurs inscrits, niveau, sets, type, environnement)
  3. Son niveau est dans l'intervalle → il clique sur « Rejoindre la partie »
  4. La modale de sélection de position s'ouvre :
    • Titre : « Choisir votre position »
    • Vue terrain avec 4 emplacements répartis entre Équipe A et Équipe B
    • Chaque emplacement indique la position (Gauche — Revés / Droite — Drive) et le joueur occupant le cas échéant
    • Les positions prises affichent le nom du joueur (ex : « Marie M. ») ou de l'invité (badge « Invité ») et sont non cliquables (grisées)
    • Les positions libres affichent une icône + et sont cliquables
  5. Le joueur clique sur un emplacement libre → celui-ci se met en surbrillance, le bouton « Confirmer ma position » s'active
  6. Le joueur confirme → requête POST /api/v1/games/{id}/join avec { team, position } → il est inscrit
  7. La modale se ferme, le terrain se met à jour en temps réel
  8. Les autres joueurs sont notifiés

Raccourci — Clic direct sur le terrain

  • Depuis l'écran de détail, un non-participant peut cliquer sur un emplacement libre du terrain (icône + avec libellé « Libre »)
  • Cela déclenche directement l'inscription avec la position sélectionnée, sans ouvrir la modale

Flux alternatif — Annulation

  • Le joueur ouvre la modale puis clique sur « Annuler » → la modale se ferme sans effet

Flux alternatif — niveau hors intervalle

  1. Son niveau est en dehors de l'intervalle → il voit « Demander à participer »
  2. Un message l'informe que son niveau est hors intervalle et qu'une demande sera envoyée
  3. Il envoie la demande → l'organisateur reçoit une notification (participation_request)
  4. L'organisateur consulte la liste des demandes via GET /api/v1/games/{id}/pending-requests
  5. L'organisateur accepte via POST /api/v1/games/{id}/players/{userId}/approve → le joueur passe en statut confirmed et reçoit une notification (participation_approved)
  6. L'organisateur refuse via POST /api/v1/games/{id}/players/{userId}/reject → le joueur est retiré de la partie et reçoit une notification (participation_rejected)

UC-PARTIE-003 · Composer les équipes

Acteur : Joueurs inscrits (partie Complète) / Créateur
Précondition : 4 joueurs inscrits

Flux — composition manuelle (changement de position)

  1. Un joueur inscrit clique sur « Changer de position » depuis l'écran de détail
  2. La modale de sélection de position s'ouvre (même modale que pour rejoindre)
  3. La position actuelle du joueur est marquée avec un badge « Occupée »
  4. Les positions prises par d'autres joueurs sont grisées (non cliquables)
  5. Le joueur sélectionne un emplacement libre → « Confirmer ma position » s'active
  6. Il confirme → requête PATCH /api/v1/games/{id}/position avec { team, position } → la position est mise à jour
  7. Le terrain se met à jour en temps réel

Flux — tirage au sort

  1. Le créateur voit un bouton « Tirage au sort » sur l'écran de détail
  2. Il clique dessus → requête POST /api/v1/games/{id}/draw
  3. L'application répartit aléatoirement les 4 joueurs (équipes + positions)
  4. Le terrain se met à jour en temps réel
  5. Les joueurs peuvent toujours modifier manuellement après le tirage

Conditions d'affichage des actions

Bouton Condition d'affichage
« Rejoindre la partie » Non participant, partie ouverte, places disponibles, niveau dans l'intervalle
« Demander à participer » Non participant, partie ouverte, places disponibles, niveau hors intervalle
Bannière « Demande en attente » + « Annuler ma demande » Participant avec statut pending (hors invitation privée)
« Changer de position » Participant inscrit (confirmed), partie pas encore « En cours »
« Tirage au sort » Créateur de la partie uniquement

Séquence API — sélection de position

sequenceDiagram
    participant J as Joueur
    participant UI as Frontend
    participant API as API

    J->>UI: Clique "Rejoindre la partie"
    UI->>UI: Ouvre modale positions
    J->>UI: Sélectionne emplacement libre
    J->>UI: Confirme
    UI->>API: POST /games/{id}/join { team, position }
    API->>API: Vérifie niveau, place dispo, position libre
    alt Niveau dans l'intervalle
        API-->>UI: 200 { data: game, pending: false }
        UI->>UI: Ferme modale, met à jour terrain
    else Niveau hors intervalle
        API-->>UI: 200 { data: game, pending: true }
        UI->>UI: Toast « Demande envoyée ! », affiche bannière d'attente
    end

UC-PARTIE-003b · Ajouter/retirer un invité

Acteur : Créateur de la partie
Précondition : Partie en statut « Ouverte », places disponibles

Flux — ajouter un invité

  1. Le créateur accède au détail de la partie (ou à l'étape de pré-remplissage lors de la création)
  2. Il sélectionne un emplacement libre et active le mode « Invité »
  3. Il saisit le nom de l'invité (max 100 caractères)
  4. POST /api/v1/games/{id}/guests avec { guest_name, team, position }
  5. L'invité apparaît sur le terrain avec un badge « Invité » et la couleur accent

Flux — retirer un invité

  1. Le créateur clique sur l'emplacement occupé par un invité
  2. Il confirme la suppression → DELETE /api/v1/games/{id}/guests/{slotId}
  3. L'emplacement redevient libre

UC-PARTIE-004 · Saisir et valider le score

Acteur : N'importe quel joueur du match
Précondition : Partie en statut « Terminée »

sequenceDiagram
    participant J1 as Joueur 1
    participant API as API
    participant J2 as Joueur 2
    participant J3 as Joueur 3
    participant J4 as Joueur 4

    J1->>API: Saisit le score (ex: 6-3, 6-4)
    API->>API: Valide le format padel
    API->>J2: Notification "Score à valider"
    API->>J3: Notification "Score à valider"
    API->>J4: Notification "Score à valider"
    J2->>API: Valide ✅
    J3->>API: Valide ✅
    J4->>API: Valide ✅
    API->>API: Score officiel → recalcul niveau
    API-->>J1: Partie archivée, niveau mis à jour

Flux alternatif — contestation

  1. Un joueur conteste → il propose un score alternatif
  2. Nouveau cycle de validation pour les 4 joueurs

Règle de timeout

  • Si un joueur ne valide pas sous 24 heures → le score est automatiquement validé

UC-PARTIE-005 · Quitter une partie

Acteur : Joueur inscrit
Précondition : Partie en statut « Ouverte » ou « Complète »

  1. Le joueur clique sur « Quitter la partie »
  2. Il est retiré de la liste des participants
  3. La partie repasse en statut « Ouverte » si elle n'est plus complète
  4. Le créateur et les autres participants sont notifiés (in-app, push, email)

UC-PARTIE-006 · Inviter un joueur à une partie privée

Acteur : Créateur de la partie
Précondition : Partie privée en statut « Ouverte » ou « Complète », utilisateur est le créateur

Flux principal — Invitation par email

  1. Le créateur accède au détail de la partie
  2. Il clique sur « Inviter un joueur »
  3. La modale d'invitation s'ouvre avec deux options : par email ou par lien
  4. Il saisit l'email du joueur et clique sur « Envoyer »
  5. API POST /api/v1/games/{id}/invitations avec { email }
  6. L'invitation est créée avec statut « En attente » et un token unique (validité : 48h)
  7. Si l'email correspond à un utilisateur existant → notification in-app

Flux alternatif — Lien partageable

  1. Le créateur clique sur l'onglet « Lien » dans la modale
  2. Il clique sur « Générer un lien »
  3. API POST /api/v1/games/{id}/invite-link → retourne un token
  4. Le lien est affiché avec un bouton « Copier le lien »
  5. Le créateur partage le lien (WhatsApp, SMS, etc.)

UC-PARTIE-007 · Accepter / Refuser une invitation

Acteur : Joueur invité
Précondition : Invitation en statut « En attente », non expirée

Flux principal — Acceptation

  1. Le joueur reçoit une notification d'invitation
  2. Il accède à ses invitations en attente (GET /api/v1/invitations/pending)
  3. Il clique sur « Accepter »
  4. API POST /api/v1/games/{gameId}/invitations/{id}/accept
  5. Le joueur est inscrit à la partie
  6. Le créateur est notifié (« X a accepté votre invitation »)

Flux alternatif — Refus

  1. Le joueur clique sur « Refuser »
  2. API POST /api/v1/games/{gameId}/invitations/{id}/decline
  3. Le créateur est notifié (« X a décliné votre invitation »)

Flux alternatif — Lien d'invitation

  1. Le joueur clique sur un lien d'invitation (route /join/:token)
  2. S'il est authentifié → API POST /api/v1/games/join-by-link/{token}
  3. Il est inscrit à la partie et redirigé vers le détail
  4. Si le lien est expiré ou déjà utilisé → message d'erreur

UC-PARTIE-008 · Rechercher des parties avec filtres avancés et géolocalisation

Acteur : Joueur (authentifié ou non pour la recherche publique)
Précondition : Aucune

Flux principal

  1. Le joueur accède à la page « Rechercher des parties » (/player/matches)
  2. Il voit la liste des parties publiques ouvertes (triées par date)
  3. Il peut basculer entre la vue liste et la vue carte (Leaflet)
  4. Il peut utiliser les filtres avancés :
    • Type : Amicale / Compétitive
    • Environnement : Intérieur, Semi-couvert, Extérieur
    • Plage de dates : date de début / date de fin
    • Niveau : niveau min / niveau max
    • Recherche textuelle : nom de club, ville ou lieu personnalisé
  5. Il peut activer la géolocalisation pour :
    • Afficher la distance de chaque partie (en km)
    • Filtrer par rayon (5, 10, 20, 50, 100 km)
    • Trier par distance au lieu de la date
  6. API GET /api/v1/games?latitude=X&longitude=Y&radius=Z&sort_by=distance&type=...

Flux alternatif — Vue carte

  1. Le joueur clique sur l'icône « Carte »
  2. Les parties géolocalisées sont affichées sur une carte Leaflet
  3. Chaque marqueur ouvre un popup avec le résumé de la partie et un bouton « Rejoindre »
  4. Le joueur peut cliquer sur le popup pour accéder au détail de la partie

Règles métier

  • La distance est calculée avec la formule de Haversine (PostgreSQL)
  • Pour les parties en club : coordonnées du club
  • Pour les parties en lieu libre : custom_latitude / custom_longitude
  • Le rayon par défaut est 50 km (recommandations), configurable pour la recherche

UC-PARTIE-009 · Parties recommandées

Acteur : Joueur authentifié
Précondition : Utilisateur connecté

Flux principal

  1. Le joueur accède à la page d'accueil (/player/home)
  2. Une section « Parties recommandées » affiche des parties adaptées à son profil
  3. API GET /api/v1/games/recommended?latitude=X&longitude=Y

Critères de recommandation

  • Niveau compatible : le niveau du joueur est dans l'intervalle [min_level, max_level] de la partie
  • Non créateur : les parties créées par l'utilisateur sont exclues
  • Non inscrit : les parties déjà rejointes sont exclues
  • Proximité : si coordonnées fournies, les parties dans un rayon de 50 km sont privilégiées
  • Clubs favoris : les parties dans les clubs favoris de l'utilisateur sont affichées en priorité
  • Tri : clubs favoris → distance → date