Add cancel RSVP feature (backend DELETE endpoint + frontend UI)
Allows guests to cancel their RSVP via a DELETE endpoint using their guestToken. Frontend shows cancel button in RsvpBar and clears local storage on success. Includes unit tests, integration tests, and E2E spec. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
74
specs/014-cancel-rsvp/data-model.md
Normal file
74
specs/014-cancel-rsvp/data-model.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Data Model: Cancel RSVP
|
||||
|
||||
**Feature**: 014-cancel-rsvp | **Date**: 2026-03-09
|
||||
|
||||
## Entities
|
||||
|
||||
### RSVP (existing — no schema changes)
|
||||
|
||||
| Field | Type | Constraints | Notes |
|
||||
|-------|------|-------------|-------|
|
||||
| id | BIGSERIAL | PK, auto-generated | Internal DB ID, never exposed |
|
||||
| rsvp_token | UUID | UNIQUE, NOT NULL | Bearer credential for the guest |
|
||||
| event_id | BIGINT | FK → events.id, NOT NULL | Links RSVP to event |
|
||||
| name | VARCHAR(100) | NOT NULL | Guest display name |
|
||||
|
||||
**No migration needed.** The cancel feature only deletes existing rows — no new columns or tables.
|
||||
|
||||
## State Transitions
|
||||
|
||||
```
|
||||
┌──────────┐ POST /events/{token}/rsvps ┌──────────┐
|
||||
│ No RSVP │ ──────────────────────────────► │ Attending │
|
||||
│ (guest) │ ◄────────────────────────────── │ (guest) │
|
||||
└──────────┘ DELETE /events/{token}/rsvps/ └──────────┘
|
||||
{rsvpToken}
|
||||
```
|
||||
|
||||
The transition is fully reversible: after cancellation, the guest can create a new RSVP (new token generated).
|
||||
|
||||
## Client-Side Storage (localStorage)
|
||||
|
||||
### StoredEvent (existing interface — no changes)
|
||||
|
||||
```typescript
|
||||
interface StoredEvent {
|
||||
eventToken: string // always present
|
||||
title: string // always present
|
||||
dateTime: string // always present
|
||||
organizerToken?: string // present if organizer
|
||||
rsvpToken?: string // present if RSVP'd — CLEARED on cancel
|
||||
rsvpName?: string // present if RSVP'd — CLEARED on cancel
|
||||
}
|
||||
```
|
||||
|
||||
### Cancel Effects on localStorage
|
||||
|
||||
| Action | rsvpToken | rsvpName | Event in list |
|
||||
|--------|-----------|----------|---------------|
|
||||
| Cancel from detail view | Removed | Removed | Kept |
|
||||
| Remove from event list | Removed | Removed | Removed |
|
||||
|
||||
## Repository Changes
|
||||
|
||||
### RsvpRepository (domain port)
|
||||
|
||||
New method:
|
||||
|
||||
```java
|
||||
/**
|
||||
* Delete an RSVP by event ID and RSVP token.
|
||||
* @return true if a record was deleted, false if not found
|
||||
*/
|
||||
boolean deleteByEventIdAndRsvpToken(Long eventId, RsvpToken rsvpToken);
|
||||
```
|
||||
|
||||
### RsvpJpaRepository
|
||||
|
||||
New method:
|
||||
|
||||
```java
|
||||
long deleteByEventIdAndRsvpToken(Long eventId, UUID rsvpToken);
|
||||
```
|
||||
|
||||
Returns count of deleted rows (0 or 1). Spring Data JPA auto-implements this derived query.
|
||||
Reference in New Issue
Block a user