Appearance
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| Statut | Sens côté participant |
|---|---|
DRAFT / CONFIGURED | La session existe mais n'a pas encore d'affectations de table calculées — rien à afficher côté participant. |
MATCHING | Le calcul des affectations est en cours côté serveur (voir « Matching asynchrone » ci-dessous) — aucune table assignée pour l'instant. |
READY | Les 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). |
ACTIVE | Une rotation est en cours — un participant inscrit a une table assignée. |
PAUSED | Rotation en cours suspendue par le staff (minuteur gelé). |
COMPLETED | La 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 unlabeloptionnel.RoundtableRotation— une itération de la session (rotationNumber, statutPENDING/ACTIVE/COMPLETED).RoundtableAssignment— l'affectation d'un participant à une table pour une rotation donnée. Important : elle référenceEventRegistration, pas directementUserouCompany(registrationId, relationregistrationversEventRegistration). Pour savoir qui est assis à une table, il faut donc traverserRoundtableAssignment.registration.user/.company— il n'y a pas de raccourci directassignment.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 noterating(1 à 5), un booléeninterestedet desnoteslibres 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, updatedAt — ne 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ération | Rôle | Comportement |
|---|---|---|
roundtableSession(eventId) | Query | Session de l'événement, config masquée (voir ci-dessus). null si aucune session n'existe encore. |
currentRoundtableAssignment(eventId) | Query | L'affectation courante du viewer, uniquement si la session est ACTIVE ou PAUSED — voir ci-dessous. |
userRoundtableAssignments(eventId) | Query | Toutes les affectations du viewer sur cet événement, toutes rotations confondues, triées par numéro de rotation croissant. |
roundtableFeedbacks(eventId) | Query | Les feedbacks donnés par le viewer (pas ceux qu'il a reçus) sur cet événement. |
createRoundtableFeedback(data) | Mutation | Crée un feedback depuis une affectation du viewer vers une autre affectation de la même table/rotation. |
updateRoundtableFeedback(data) | Mutation | Modifie 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 :
assignmentId≠targetAssignmentId— impossible de se noter soi-même.- Les deux affectations existent.
assignmentIddoit appartenir au viewer (viaregistration.userId) — sinonForbiddenException(« You can only give feedback from your own assignment »). Ne tentez pas de créer un feedback pour le compte d'un autre participant.- Les deux affectations doivent partager la même
rotationIdet la mêmetableId— 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 (
DRAFTouCONFIGURED) plutôt que de rester bloquée enMATCHING. 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/CONFIGUREDsans jamais atteindreREADYsignifie 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
MATCHINGen 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 enMATCHINGsans réponse à un état exploitable par le staff. Un client qui verrait une session rester enMATCHINGanormalement 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.