Skip to content

feat: add Guest RSVP URLs tab for managing guest RSVP links - #218

Merged
qiyundai merged 4 commits into
devfrom
feat/guest-rsvp-links
Jul 24, 2026
Merged

feat: add Guest RSVP URLs tab for managing guest RSVP links#218
qiyundai merged 4 commits into
devfrom
feat/guest-rsvp-links

Conversation

@qiyundai

@qiyundai qiyundai commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • "Guest RSVP URLs" tab (4th tab, alongside Registrations/Campaigns/Sessions) on the Attendee Dashboard for managing one-time-use RSVP tokens — tokens that let a guest register for an event without Adobe ID login (companion event-libs PR handles the guest-facing submission).
  • Four authenticated apiService methods on the rsvpTokens resource: getRsvpTokens, generateRsvpToken, updateRsvpToken (PATCH, extends expiry by a relative day count), revokeRsvpToken — all on the existing esp host/eventScopeCheck-protected route family. getRsvpTokens returns a bare array (no wrapper object).
  • ESP never returns a composed URL for a token — only the raw string — so the shareable guest link is now built client-side as ${event.detailPagePath}?rsvpToken=${token}, mirroring the existing preview-URL composition pattern already used in EventForm.tsx/EventsDashboard.tsx.
  • Status model is unused | used | revoked plus a separate isExpired boolean (an unused-but-past-expiry token still reports status: 'unused' — it stays extendable/revocable, and the UI shows it as "Expired" via a display-only computed status so it doesn't look like a healthy link).
  • Extending a token sends a relative expiresInDays instead of an absolute epoch; the server recomputes and replaces expiresAt as now + expiresInDays.
  • Table: URL + copy, status badge, creator, created/expires dates, who used it, with generate/extend/revoke actions. Extend is gated on event:write; Revoke (irreversible) is gated on event:delete, matching the precedent set by EventsDashboard/SpeakersDashboard for destructive actions.
  • Campaign attribution is fully decoupled from the token itselfRsvpToken no longer models campaignId at all, and tokens are always generated with no bound campaign. Instead, clicking "Copy" on a token's link shows a dialog (only when the event has active campaigns) asking whether to track that particular share with one; picking a campaign composes the copied URL from that campaign's own shareable URL with an additional rsvpToken=<token> param appended — two fully independent query params, not a relationship stored on the token record. Picking "No campaign" (the default) copies the plain token link unchanged.

Update (BE contract now finalized and merged): this PR most recently targeted a guestRsvpTokens resource with a ?guestToken= query param, then a pure rename to rsvpTokens/?rsvpToken= vocabulary. The backend's RSVP-token feature has now merged to develop for real (events-service-platform PR #1132). Verifying this implementation directly against the shipped OpenAPI schema surfaced a real field-name bug: RsvpToken used createdAt/usedByAttendeeId (from an earlier, simplified draft of the integration guide), but the actual schema uses creationTime/modificationTime/usedByAttendee plus revokedAt/revokedBy — fixed to match, along with the two table columns that read them. Separately, per a team decision to stop coupling campaign attribution to the token record (a backend refactor is planned separately), added the share-time campaign-tracking dialog described above and removed campaignId from the token types entirely.

Test plan

  • npm run type-check (tsc --noEmit) clean
  • npm run check (lint + type-check) clean — note: this repo's lint script currently excludes web-src entirely and has no working TS/React ESLint config for it either way (pre-existing, not introduced here — confirmed again this round)
  • npm run test:unit — 73/73 passing (no test coverage exists yet for this tab specifically; matches this repo's existing test conventions for other Registrations tabs)
  • Reviewed across three rounds of independent multi-agent review on the original implementation; fixed: an unhandled promise rejection on failed link generation, stale cross-event data shown while switching events, a magic-number unit bug risk in the extend-TTL date math, the missing PATCH entry in the non-invasive-test allowlist, and the Revoke permission gate (was event:write, now event:delete) plus its useMemo dependency array.
  • Re-reviewed across two further rounds after migrating to the guestRsvpTokens contract (including a cross-repo pass checking the guestToken query param and URL composition against the sibling event-libs repo); no additional issues found on the EMC side.
  • Re-reviewed again after the wire-identifier rename to rsvpToken/rsvpTokens vocabulary, including a whole-repo grep for every old identifier/string and a check that the file rename (guestRsvp.tsrsvpToken.ts) left no stale importers; no issues found.
  • Re-verified against the now-finalized, merged BE contract; found and fixed the field-name mismatch and implemented the campaign/token decoupling dialog. One further independent review round (URL composition correctness, hook dependency correctness, sentinel-value collision risk, prop freshness) found no issues.
  • Manual verification in a running EMC instance against the live/staged rsvpTokens endpoints.

🤖 Generated with Claude Code

qiyundai and others added 4 commits July 21, 2026 12:30
Adds a 4th tab to the Attendee Dashboard (alongside Registrations,
Campaigns, Sessions) so Event Marketers can generate, list, extend the
expiration of, and revoke one-time-use guest RSVP links — links that
let a guest register for an event without Adobe ID login (companion
event-libs PR handles the guest-facing redemption flow).

Adds four new authenticated API methods (generate/list/PATCH-extend/
revoke) on the existing eventScopeCheck-protected route family, and
widens the API client's method support to include PATCH end-to-end
(including the non-invasive-test-mode safety guard, which previously
only covered PUT/POST/DELETE).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The backend renamed the resource guestRsvpLinks -> guestRsvpTokens, changed
the list endpoint to return a bare array instead of a wrapper object, and
stopped returning a composed URL (ESP now returns only the raw token, so
EMC composes the shareable link client-side as
`${event.detailPagePath}?guestToken=${token}`, mirroring the existing
EventForm/EventsDashboard preview-URL pattern). Status values are now
unused/used/revoked plus a separate isExpired flag (an unused-but-expired
token still needs to stay extendable/revocable), and extending a token now
takes a relative expiresInDays instead of an absolute epoch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The backend dropped the "guest" prefix from the RSVP token contract:
the marketer-facing path moved from .../guestRsvpTokens to
.../rsvpTokens and the shareable link's query param from ?guestToken=
to ?rsvpToken=. Renames api.ts's methods, the guestRsvp.ts type file
(now rsvpToken.ts), and Registrations.tsx's state/handlers to match.
Visible "Guest RSVP" product copy and the GuestRsvpUrlsTab component
name are intentionally kept; no behavior changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…from tokens

Verified against the now-merged BE contract (events-service-platform
PR #1132) and made two corrections:

- RsvpToken's field names didn't match the real schema (an earlier
  simplified draft of the integration guide used createdAt/
  usedByAttendeeId; the actual API uses creationTime/modificationTime/
  usedByAttendee, plus revokedAt/revokedBy). Renamed to match, and
  updated the two table columns that read them.
- Per a product decision to stop binding campaignId to a token at
  generation time (the BE coupling is being refactored separately),
  removed campaignId entirely from RsvpToken/RsvpTokenCreatePayload.
  Campaign attribution is now opted into per-copy: clicking Copy on a
  token shows a dialog (only when the event has active campaigns) to
  optionally track the share with one, composing the copied URL from
  that campaign's own shareable URL plus an appended rsvpToken param
  — two independent query params, not a bound relationship.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@qiyundai
qiyundai merged commit 42e9e21 into dev Jul 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant