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:
90
specs/014-cancel-rsvp/plan.md
Normal file
90
specs/014-cancel-rsvp/plan.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Implementation Plan: Cancel RSVP
|
||||
|
||||
**Branch**: `014-cancel-rsvp` | **Date**: 2026-03-09 | **Spec**: [spec.md](spec.md)
|
||||
**Input**: Feature specification from `/specs/014-cancel-rsvp/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Allow guests to cancel their RSVP either explicitly from the event detail view (tap-to-reveal pattern on the RSVP bar) or implicitly when removing an RSVP'd event from their event list. The backend provides an idempotent DELETE endpoint; the frontend handles confirmation, API call, localStorage cleanup, and UI state reset.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: Java 25 (backend), TypeScript 5.9 (frontend)
|
||||
**Primary Dependencies**: Spring Boot 3.5.x, Vue 3, Vue Router 5, openapi-fetch, openapi-typescript
|
||||
**Storage**: PostgreSQL (JPA via Spring Data, Liquibase migrations)
|
||||
**Testing**: JUnit + Mockito (backend unit), MockMvc + Testcontainers (backend integration), Vitest (frontend unit), Playwright + MSW (frontend E2E)
|
||||
**Target Platform**: Self-hosted PWA (web browser, mobile-first)
|
||||
**Project Type**: Web application (hexagonal backend + SPA frontend)
|
||||
**Performance Goals**: Cancel operation < 500ms server-side
|
||||
**Constraints**: Privacy by design (no analytics), token-based auth (no login), idempotent delete
|
||||
**Scale/Scope**: Single new endpoint, 3 modified frontend components, 1 new composable method
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Principle | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| I. Privacy by Design | PASS | No new PII stored. Delete operation removes data. No analytics. |
|
||||
| II. Test-Driven Methodology | PASS | Plan includes TDD cycle: tests before implementation. E2E mandatory. |
|
||||
| III. API-First Development | PASS | OpenAPI spec updated first, types generated before implementation. |
|
||||
| IV. Simplicity & Quality | PASS | Minimal changes to existing code. No new abstractions. Idempotent delete is the simplest correct approach. |
|
||||
| V. Dependency Discipline | PASS | No new dependencies required. |
|
||||
| VI. Accessibility | PASS | Interactive elements will use semantic HTML, ARIA, keyboard navigation. Confirm dialog already accessible. |
|
||||
|
||||
**Gate result**: ALL PASS — proceed to Phase 0.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/014-cancel-rsvp/
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0 output
|
||||
├── data-model.md # Phase 1 output
|
||||
├── contracts/ # Phase 1 output
|
||||
│ └── cancel-rsvp.yaml # DELETE endpoint contract
|
||||
└── tasks.md # Phase 2 output (by /speckit.tasks)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
backend/
|
||||
├── src/main/java/de/fete/
|
||||
│ ├── domain/
|
||||
│ │ ├── model/Rsvp.java # existing (no changes)
|
||||
│ │ ├── model/RsvpToken.java # existing (no changes)
|
||||
│ │ └── port/
|
||||
│ │ ├── in/CancelRsvpUseCase.java # NEW use case port
|
||||
│ │ └── out/RsvpRepository.java # MODIFY (add deleteByRsvpToken)
|
||||
│ ├── application/service/RsvpService.java # MODIFY (implement cancel)
|
||||
│ └── adapter/
|
||||
│ ├── in/web/EventController.java # MODIFY (add DELETE handler)
|
||||
│ └── out/persistence/
|
||||
│ ├── RsvpJpaRepository.java # MODIFY (add deleteByRsvpToken)
|
||||
│ └── RsvpPersistenceAdapter.java # MODIFY (implement delete)
|
||||
├── src/main/resources/openapi/api.yaml # MODIFY (add DELETE endpoint)
|
||||
└── src/test/java/de/fete/
|
||||
├── application/service/RsvpServiceTest.java # MODIFY (add cancel tests)
|
||||
└── adapter/in/web/EventControllerIntegrationTest.java # MODIFY (add cancel tests)
|
||||
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── api/schema.d.ts # REGENERATE (from updated OpenAPI)
|
||||
│ ├── components/
|
||||
│ │ ├── RsvpBar.vue # MODIFY (tap-to-reveal cancel)
|
||||
│ │ ├── EventList.vue # MODIFY (conditional dialog msg)
|
||||
│ │ └── ConfirmDialog.vue # existing (no changes)
|
||||
│ ├── composables/useEventStorage.ts # MODIFY (add removeRsvp)
|
||||
│ └── views/EventDetailView.vue # MODIFY (add cancel logic)
|
||||
└── e2e/
|
||||
└── cancel-rsvp.spec.ts # NEW (E2E tests)
|
||||
```
|
||||
|
||||
**Structure Decision**: Follows existing hexagonal architecture. New use case port + implementation in existing service. Single new endpoint added to existing controller.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
No constitution violations — table not needed.
|
||||
Reference in New Issue
Block a user