Skip to content

Roundtables

Une roundtable (« table ronde ») est une session de speed-networking structurée rattachée à un événement (Event) : les participants inscrits sont répartis par petites tables, tournent d'une table à l'autre à intervalles réguliers (« rotations »), et peuvent laisser un retour sur les personnes rencontrées. Le staff configure et pilote la session ; cette page décrit uniquement ce qu'un participant (rôle USER) peut interroger et faire via l'API — pas les opérations de pilotage staff (création de session, démarrage, avance manuelle des rotations, etc.), qui font l'objet d'une documentation séparée à usage interne.

Une session par événement

Un événement porte au plus une RoundtableSession. Il n'y a donc jamais qu'une seule session à interroger par eventId — pas de liste, pas d'historique de sessions multiples pour un même événement.

Cycle de vie

Le statut de la session (RoundtableSession.status, énum RoundtableStatus) suit une progression linéaire, avec une boucle possible entre ACTIVE et PAUSED :

DRAFT → CONFIGURED → MATCHING → READY → ACTIVE ⇄ PAUSED → COMPLETED
StatutSens côté participant
DRAFT / CONFIGUREDLa session existe mais n'a pas encore d'affectations de table calculées — rien à afficher côté participant.
MATCHINGLe calcul des affectations est en cours côté serveur (voir « Matching asynchrone » ci-dessous) — aucune table assignée pour l'instant.
READYLes affectations de la première rotation existent, mais la session n'a pas encore démarré (le staff n'a pas encore lancé le minuteur).
ACTIVEUne rotation est en cours — un participant inscrit a une table assignée.
PAUSEDRotation en cours suspendue par le staff (minuteur gelé).
COMPLETEDLa session est terminée ; les affectations restent consultables a posteriori.

Modèle de données

Cinq modèles composent une session, tous rattachés en cascade à RoundtableSession :

  • RoundtableTable — une table physique de la session, numérotée (tableNumber), avec un label optionnel.
  • RoundtableRotation — une itération de la session (rotationNumber, statut PENDING/ACTIVE/COMPLETED).
  • RoundtableAssignment — l'affectation d'un participant à une table pour une rotation donnée. Important : elle référence EventRegistration, pas directement User ou Company (registrationId, relation registration vers EventRegistration). Pour savoir qui est assis à une table, il faut donc traverser RoundtableAssignment.registration.user / .company — il n'y a pas de raccourci direct assignment.user.
  • RoundtableFeedback — un retour laissé par le titulaire d'une affectation (assignmentId) sur un autre participant de la même table/rotation (targetAssignmentId), avec une note rating (1 à 5), un booléen interested et des notes libres optionnelles. Une paire (assignmentId, targetAssignmentId) est unique — un seul feedback par personne rencontrée et par rotation.

Ce que roundtableSession renvoie à un participant

roundtableSession(eventId) renvoie la session de l'événement (ou null si aucune n'existe), mais les champs de configuration du matching ne sont pas exposés aux participants : ils sont masqués si l'appelant n'est ni administrateur ni staff de cet événement précis, et ne sont retournés que pour un viewer staff. Un client participant reçoit toujours id, eventId, status, tableCount, seatsPerTable, rotationCount, rotationDuration, useTimer, noRepeatMode, currentRotation, rotationStartedAt, rotationExtraSeconds, startedAt, completedAt, createdAt, updatedAtne construisez pas d'UI qui dépend des champs de configuration du matching (stratégie, pondérations, prompt), ils ne seront simplement pas présents pour un utilisateur non-staff.

Queries et mutations disponibles pour un participant

Ces opérations n'exigent qu'un token valide et un compte de rôle USER.

OpérationRôleComportement
roundtableSession(eventId)QuerySession de l'événement, config masquée (voir ci-dessus). null si aucune session n'existe encore.
currentRoundtableAssignment(eventId)QueryL'affectation courante du viewer, uniquement si la session est ACTIVE ou PAUSED — voir ci-dessous.
userRoundtableAssignments(eventId)QueryToutes les affectations du viewer sur cet événement, toutes rotations confondues, triées par numéro de rotation croissant.
roundtableFeedbacks(eventId)QueryLes feedbacks donnés par le viewer (pas ceux qu'il a reçus) sur cet événement.
createRoundtableFeedback(data)MutationCrée un feedback depuis une affectation du viewer vers une autre affectation de la même table/rotation.
updateRoundtableFeedback(data)MutationModifie rating/interested/notes d'un feedback déjà créé par le viewer.

currentRoundtableAssignment — uniquement pendant une rotation active

currentRoundtableAssignment ne cherche une session que parmi celles au statut ACTIVE ou PAUSED ; en dehors de cette fenêtre (session DRAFT/CONFIGURED/MATCHING/READY/COMPLETED), la query renvoie null même si le viewer a bien une affectation existante pour la rotation courante. Elle renvoie aussi null si l'appelant n'a pas d'EventRegistration sur l'événement, ou n'a pas d'affectation pour la rotation en cours (currentRotation de la session).

Quand une affectation est trouvée, la réponse est enrichie côté serveur avec la liste des autres personnes assises à la même table pour la même rotation (table.assignments) — chaque voisin porte sa propre registration.user/registration.company (nom, ville, logo, skills/interestedSkills). C'est le moyen prévu de savoir « qui est à ma table » sans requête séparée.

Pour l'historique complet (rotations passées incluses), utilisez userRoundtableAssignments(eventId) plutôt que currentRoundtableAssignment — cette query n'est pas limitée par le statut de la session et applique le même enrichissement table.assignments par voisinage.

affinityWithViewer — score d'affinité sur une affectation

Le type RoundtableAssignment porte un champ résolu affinityWithViewer (RoundtablePairAffinityType { score: Float!, reason: String }), disponible sur les affectations retournées par currentRoundtableAssignment/userRoundtableAssignments. Il renvoie null si l'affectation cible n'a pas d'entreprise associée, si le viewer n'a lui-même pas d'entreprise, ou si le viewer et la cible appartiennent à la même entreprise — dans ces cas, ne pas afficher de score plutôt que d'en interpréter l'absence.

Feedback — règles de validation

createRoundtableFeedback vérifie, dans cet ordre :

  1. assignmentIdtargetAssignmentId — impossible de se noter soi-même.
  2. Les deux affectations existent.
  3. assignmentId doit appartenir au viewer (via registration.userId) — sinon ForbiddenException (« You can only give feedback from your own assignment »). Ne tentez pas de créer un feedback pour le compte d'un autre participant.
  4. Les deux affectations doivent partager la même rotationId et la même tableId — impossible de noter quelqu'un qui n'était pas à la même table pour la même rotation.

updateRoundtableFeedback vérifie de la même façon que le feedback édité appartient bien au viewer (via assignment.registration.userId), sinon ForbiddenException (« You can only edit your own feedback »). Seuls rating, interested et notes sont modifiables — pas assignmentId/targetAssignmentId.

La contrainte d'unicité (assignmentId, targetAssignmentId) signifie qu'un deuxième appel à createRoundtableFeedback pour la même paire échouera : utilisez updateRoundtableFeedback pour corriger un feedback déjà envoyé plutôt que d'en recréer un.

Matching asynchrone — écouter, ne pas sonder en boucle

Le calcul des affectations de table (déclenché côté staff) est asynchrone : dès le déclenchement, la session passe immédiatement au statut MATCHING et l'opération retourne sans attendre la fin du calcul. Le calcul peut prendre un moment ; le client ne doit pas bloquer sur la réponse de la mutation de déclenchement en attendant les tables.

Pour un client participant, la bonne pratique est d'écouter le canal WebSocket roundtableMatchingCompleted (diffusé sur la room event:{eventId}:participants, voir WebSocket) plutôt que de sonder roundtableSession(eventId).status en boucle serrée. À réception, refaites une requête roundtableSession/currentRoundtableAssignment pour récupérer les tables fraîchement calculées.

Deux points à connaître pour un affichage robuste :

  • En cas d'échec du calcul, la session repasse à son statut précédent (DRAFT ou CONFIGURED) plutôt que de rester bloquée en MATCHING. Le canal WebSocket qui signale explicitement l'échec (roundtableMatchingFailed) est réservé au staff — un client participant ne le recevra pas. Pour détecter un échec côté participant, constatez plutôt qu'une session repassée à DRAFT/CONFIGURED sans jamais atteindre READY signifie que le calcul n'a pas abouti (à re-signaler côté UI comme « en attente du staff », pas comme une erreur transitoire à ignorer).
  • Une session ne reste jamais indéfiniment bloquée en MATCHING en cas d'incident serveur (crash pendant le calcul, par exemple) : un mécanisme de récupération côté serveur ramène automatiquement une session restée trop longtemps en MATCHING sans réponse à un état exploitable par le staff. Un client qui verrait une session rester en MATCHING anormalement longtemps peut donc continuer à attendre l'événement WebSocket ou revérifier périodiquement le statut à basse fréquence — pas besoin de logique de timeout côté client.

Minuteur de rotation — serveur-autoritaire

Le temps restant d'une rotation n'est jamais calculé côté client : il se dérive des champs rotationStartedAt et rotationExtraSeconds exposés sur RoundtableSession (durée = rotationDuration minutes + rotationExtraSeconds secondes, à partir de rotationStartedAt), combinés à la durée configurée rotationDuration. N'implémentez pas de compte à rebours indépendant basé sur l'horloge du client : il divergerait progressivement de l'état serveur (pause, prolongation). Recalculez plutôt le temps restant à partir de ces champs à chaque réception d'un événement WebSocket lié à la rotation (démarrage, prolongation, reset du minuteur — voir WebSocket), en refaisant la query pour obtenir les valeurs à jour.

Le champ rotationExtraSeconds change quand le staff prolonge une rotation en cours — c'est le seul ajustement de durée exposé par roundtableSession ; la comptabilité fine des pauses (rotationPausedAt, rotationTotalPausedMs) est un détail de calcul interne, non retourné par cette query. Dans tous les cas, la source de vérité du temps restant reste le serveur, jamais un minuteur local.