[Frontend] Add a web RSVP control — RSVP has no web surface at all #370
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Impact: CRITICAL
Found in the August 2026 session lifecycle review (#319).
What the user experiences
RSVP is a named step of the core session lifecycle, and the web app has no control for it whatsoever. A player who is not on Discord, or who has not linked their Discord account, has no way to tell the GM they're coming. The only web-side attendance controls are GM tools gated to completed sessions, which is the opposite of what RSVP needs (a pre-session signal from the player).
Evidence
webapp/frontend/src/pages/SessionDetail.jsx:1384-1411— the only web attendance control is GM-only and gated onsession.status === "completed".webapp/frontend/src/pages/RecordingDashboard.jsx:414-441— a second, GM-only, differently-labelled copy of the same idea, live-session only.bot/questboard_bot/cogs/notifications.py:386-413) that never references the poll it resolved, and acknowledges nothing on either success or failure (see the companion "acknowledge Discord votes and RSVPs" issue in this milestone).Why it matters for a hosted product
A hosted customer whose group isn't Discord-native — or whose players haven't linked their accounts — cannot use RSVP at all today. This is the single biggest hole in the product's own stated core loop, and it blocks anyone selling the product as usable without Discord.
Proposed fix
On
SessionDetail, for anyconfirmedortentativesession, show a three-state "Are you in?" control for the current user, writing to the same attendance table the Discord reactions already write to. Show the party's responses as a roster underneath. This is the audit's P3.Acceptance criteria
SessionDetailshows a self-service RSVP control (yes/maybe/no) for the signed-in user onconfirmed/tentativesessions.Picking this up as v4.3.0 phase 4 (#514), first on the lane, followed by #380 and #368. Decisions: a member-facing endpoint on the same table the Discord reactions write to, a three-state "Are you in?" control on confirmed and tentative sessions with the party's roster underneath, an inline confirmation after saving, and the same notification the Discord path already publishes when an RSVP changes (no new event type). The GM's completed-session attendance tools stay as they are.
Done in PR #526 (auto-merging on green); ships with v4.3.0.
Reused the row the Discord ✅/❌ reaction already writes. That path calls
PUT /api/bot/sessions/{id}/attendance/{discord_id}→session_attendance.attended, aNOT NULL DEFAULT falseboolean, so "said no" and "never answered" were the same value. Added a nullablersvp_statusenum andrsvp_atto the same table (migrationfa0b1c2d3e4f, chained after #369'sf0ab1c2d3e4f), because NULL-as-"no answer" is what the roster and #368 both key on.GET /api/sessions/{id}/rsvps: one entry per campaign member,status: nullfor the unanswered, readable by any member.PUT /api/sessions/{id}/rsvp{"status": "yes"|"maybe"|"no"}: members only, for themselves only (no user id in the path). Open on confirmed sessions and on tentative-mode sessions still proposed; a vote-mode proposed session is excluded because its time-slot vote already asks the question.rsvp_statusalongside its unchanged effect onattended, so a reaction in Discord shows in the web roster. Purely additive; no bot code change, no contract bump.One decision worth recording: the RSVP path does not touch
attended. Mirroring yes→true would match the bot exactly, but it asserts someone attended a session that has not happened, and, becausebulk_insert_attendance_absentnever overwrites an existing row, it would silently disable the #114 recording auto-fill for every member who RSVPed. The GM's completed-session tools are unchanged.No notification is published on a web RSVP: the Discord path publishes no event either, so there was nothing to mirror and nothing was invented.
UI:
SessionRsvp.jsx, first thing in the session page's main column. Yes/Maybe/No segmented control, party-wide roster with an explicit "No answer" state, inline confirmation, noalert(). Tests: 22 backend, 9 component, 2 page-wiring. A follow-up is filed as #525: feeding an RSVP of "no" into the at-risk evaluation, which currently counts only slot votes.