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>
75 lines
2.4 KiB
Markdown
75 lines
2.4 KiB
Markdown
# 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.
|