Files
fete/specs/014-cancel-rsvp/tasks.md
nitrix 41bb17d5c9
All checks were successful
CI / backend-test (push) Successful in 59s
CI / frontend-test (push) Successful in 24s
CI / frontend-e2e (push) Successful in 1m18s
CI / build-and-publish (push) Has been skipped
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>
2026-03-12 17:45:37 +01:00

12 KiB

Tasks: Cancel RSVP

Input: Design documents from /specs/014-cancel-rsvp/ Prerequisites: plan.md (required), spec.md (required), research.md, data-model.md, contracts/

Tests: Included — constitution mandates TDD (Red → Green → Refactor). E2E tests mandatory per principle II.

Organization: Tasks are grouped by user story to enable independent implementation and testing of each story.

Format: [ID] [P?] [Story] Description

  • [P]: Can run in parallel (different files, no dependencies)
  • [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
  • Include exact file paths in descriptions

Phase 1: Setup (API Contract & Type Generation)

Purpose: Define the DELETE endpoint contract and regenerate types for both backend and frontend.

  • T001 Add DELETE /events/{token}/rsvps/{rsvpToken} endpoint to backend/src/main/resources/openapi/api.yaml per specs/014-cancel-rsvp/contracts/cancel-rsvp.yaml — operationId cancelRsvp, responses 204 (success/idempotent) and 500
  • T002 Regenerate backend API interfaces from updated OpenAPI spec via cd backend && ./mvnw generate-sources
  • T003 Regenerate frontend TypeScript types from updated OpenAPI spec via cd frontend && npm run generate:api

Phase 2: Foundational (Backend Delete Infrastructure)

Purpose: Backend repository and service layer for RSVP deletion — blocks all user stories.

⚠️ CRITICAL: No user story work can begin until this phase is complete.

Tests (write FIRST, must FAIL before implementation)

  • T004 [P] Write unit test for cancelRsvp() in backend/src/test/java/de/fete/application/service/RsvpServiceTest.java — test cases: successful delete (returns true), RSVP not found (returns false), event not found (returns false)
  • T005 [P] Write integration test for DELETE /api/events/{token}/rsvps/{rsvpToken} in backend/src/test/java/de/fete/adapter/in/web/EventControllerIntegrationTest.java — test cases: 204 on successful delete, 204 on already-deleted RSVP (idempotent), 204 when event not found (idempotent), verify RSVP row actually removed from DB

Implementation

  • T006 Create CancelRsvpUseCase interface in backend/src/main/java/de/fete/domain/port/in/CancelRsvpUseCase.java — single method cancelRsvp(EventToken, RsvpToken) returning void
  • T007 Add deleteByEventIdAndRsvpToken(Long eventId, RsvpToken rsvpToken) to domain port backend/src/main/java/de/fete/domain/port/out/RsvpRepository.java
  • T008 Add deleteByEventIdAndRsvpToken(Long eventId, UUID rsvpToken) derived delete query to backend/src/main/java/de/fete/adapter/out/persistence/RsvpJpaRepository.java
  • T009 Implement deleteByEventIdAndRsvpToken() in backend/src/main/java/de/fete/adapter/out/persistence/RsvpPersistenceAdapter.java — delegate to JPA repository, return boolean (deleted count > 0)
  • T010 Implement CancelRsvpUseCase in backend/src/main/java/de/fete/application/service/RsvpService.java — look up event by token, if found call repository delete, no error on not-found (idempotent). Add @Transactional
  • T011 Implement cancelRsvp() handler in backend/src/main/java/de/fete/adapter/in/web/EventController.java — accept event token and RSVP token path params, call use case, return 204 No Content
  • T012 Run cd backend && ./mvnw verify — all tests (existing + new) must pass

Checkpoint: Backend DELETE endpoint functional and tested. Verify via integration tests.


Phase 3: User Story 1 — Cancel RSVP from Event Detail View (Priority: P1) 🎯 MVP

Goal: Guest can tap the "You're attending!" bar to reveal a cancel button, confirm cancellation, and have RSVP deleted server-side with UI reset.

Independent Test: Navigate to event detail as RSVP'd guest → tap status bar → tap cancel → confirm → verify RSVP deleted, attendee count decremented, bar reset to CTA state. Then re-RSVP to verify flow works again.

Tests (write FIRST, must FAIL before implementation)

  • T013 [US1] Write E2E test file frontend/e2e/cancel-rsvp.spec.ts — US1 scenarios: (1) status bar shows cancel affordance when RSVP'd, (2) tap reveals cancel button, (3) confirm cancellation → 204 → localStorage cleared + count decremented + bar reset, (4) server error → error message + state unchanged, (5) re-RSVP after cancel works

Implementation

  • T014 [US1] Add removeRsvp(eventToken: string) method to frontend/src/composables/useEventStorage.ts — clears rsvpToken and rsvpName from the stored event without removing the event from the list
  • T015 [US1] Modify frontend/src/components/RsvpBar.vue — make status state tappable with tap-to-reveal pattern: (1) add expanded state, (2) tapping "You're attending!" toggles expanded, (3) expanded state shows "Cancel attendance" button, (4) emit cancel event on button click, (5) collapse on outside click/Escape, (6) add subtle chevron icon hint, (7) ARIA: role="button", aria-expanded, keyboard support (Enter/Space to toggle, Escape to collapse)
  • T016 [US1] Add cancel RSVP logic to frontend/src/views/EventDetailView.vue — (1) handle cancel emit from RsvpBar, (2) show ConfirmDialog with message "Your attendance will be permanently cancelled.", (3) on confirm: call api.DELETE('/events/{token}/rsvps/{rsvpToken}'), (4) on 204: call removeRsvp(), decrement attendee count, reset RSVP state (rsvpName = ''), (5) on error: show error message "Could not cancel RSVP. Please try again.", (6) keep local state unchanged on error
  • T017 [US1] Run frontend unit tests cd frontend && npm run test:unit and E2E tests cd frontend && npx playwright test cancel-rsvp — all must pass

Checkpoint: US1 fully functional. Guest can cancel RSVP from event detail view and re-RSVP.


Phase 4: User Story 2 — Auto-Cancel on Event List Removal (Priority: P2)

Goal: When a guest removes an RSVP'd event from their event list, the confirmation dialog warns about attendance cancellation and the RSVP is deleted server-side before localStorage cleanup.

Independent Test: Add RSVP'd event to list → initiate removal → verify dialog mentions attendance → confirm → verify server DELETE called → event removed from list. Also test: event without RSVP shows standard dialog (no mention of attendance).

Tests (write FIRST, must FAIL before implementation)

  • T018 [US2] Add US2 E2E scenarios to frontend/e2e/cancel-rsvp.spec.ts — (1) removal of RSVP'd event shows "attendance will be cancelled" in dialog, (2) removal of non-RSVP'd event shows standard dialog (no attendance mention), (3) confirm removal → DELETE called → event removed from list, (4) server error on DELETE → error message + event stays in list, (5) dismiss dialog → no changes

Implementation

  • T019 [US2] Modify frontend/src/components/EventList.vue — (1) detect if pending-delete event has RSVP token via getRsvp(eventToken), (2) set conditional dialog message: with RSVP → "This event will be removed from your list and your attendance will be cancelled." / without RSVP → existing message, (3) make confirmDelete() async: if RSVP exists, call api.DELETE('/events/{token}/rsvps/{rsvpToken}') first, (4) on success or 404: proceed with removeEvent(), (5) on other error: show error message, abort removal
  • T020 [US2] Import API client and getRsvp from useEventStorage in frontend/src/components/EventList.vue — ensure API client is available for server calls
  • T021 [US2] Run E2E tests cd frontend && npx playwright test cancel-rsvp — all US1 + US2 scenarios must pass

Checkpoint: US1 + US2 functional. Both cancel paths work independently.


Phase 5: User Story 3 — Cancel RSVP with Expired/Invalid Token (Priority: P3)

Goal: Gracefully handle stale RSVP tokens — treat "not found" server responses as successful cancellation.

Independent Test: Set stale RSVP token in localStorage → attempt cancel from detail view → verify 404 treated as success → localStorage cleaned. Same for event list removal.

Tests (write FIRST, must FAIL before implementation)

  • T022 [US3] Add US3 E2E scenarios to frontend/e2e/cancel-rsvp.spec.ts — (1) cancel from detail view with stale token (server 404) → treated as success, localStorage cleaned, UI reset, (2) event list removal with stale token (server 404) → treated as success, event removed

Implementation

  • T023 (already implemented in T016 — 404 treated as success) [US3] Update cancel logic in frontend/src/views/EventDetailView.vue — treat 404 response from DELETE as success (RSVP already gone): clean up localStorage and reset UI same as 204
  • T024 (already implemented in T019 — 404 treated as success) [US3] Update cancel logic in frontend/src/components/EventList.vue — treat 404 response from DELETE as success: proceed with removeEvent() (note: this may already be handled if T019 implemented "success or 404" correctly — verify and adjust if needed)
  • T025 [US3] Run all E2E tests cd frontend && npx playwright test cancel-rsvp — all US1 + US2 + US3 scenarios must pass

Checkpoint: All user stories functional. Edge cases handled gracefully.


Phase 6: Polish & Cross-Cutting Concerns

Purpose: Final verification and cleanup across all stories.

  • T026 Run full backend verify cd backend && ./mvnw verify — checkstyle + all tests
  • T027 Run full frontend test suite cd frontend && npm run test:unit && npx playwright test — all unit + E2E tests
  • T028 Verify accessibility: RsvpBar cancel interaction is keyboard-navigable (Tab, Enter/Space, Escape), ARIA attributes correct, confirm dialog focus management works

Dependencies & Execution Order

Phase Dependencies

  • Setup (Phase 1): No dependencies — start immediately
  • Foundational (Phase 2): Depends on Phase 1 (generated API interfaces needed)
  • US1 (Phase 3): Depends on Phase 2 (backend endpoint must exist)
  • US2 (Phase 4): Depends on Phase 2 (backend endpoint must exist). Independent of US1.
  • US3 (Phase 5): Depends on US1 and US2 (refines their error handling)
  • Polish (Phase 6): Depends on all stories complete

User Story Dependencies

  • US1 (P1): Independent after Phase 2 — core cancel flow
  • US2 (P2): Independent after Phase 2 — can be implemented in parallel with US1
  • US3 (P3): Depends on US1 + US2 — refines error handling in both

Within Each User Story

  • Tests MUST be written and FAIL before implementation
  • Composable/model changes before component changes
  • Component changes before view integration
  • Story complete before moving to next priority

Parallel Opportunities

  • T004 + T005 (backend tests) can run in parallel
  • T006 + T007 (use case + repository port) can run in parallel
  • US1 and US2 can be implemented in parallel after Phase 2 (different files)
  • T026 + T027 (backend verify + frontend tests) can run in parallel

Parallel Example: Phase 2 (Foundational)

# Write tests in parallel:
Task T004: "Unit test for cancelRsvp in RsvpServiceTest.java"
Task T005: "Integration test for DELETE endpoint in EventControllerIntegrationTest.java"

# Then implement sequentially: T006 → T007 → T008 → T009 → T010 → T011 → T012

Parallel Example: US1 + US2

# After Phase 2 completes, launch in parallel:
# Stream A (US1): T013 → T014 → T015 → T016 → T017
# Stream B (US2): T018 → T019 → T020 → T021

Implementation Strategy

MVP First (User Story 1 Only)

  1. Complete Phase 1: Setup (OpenAPI + type generation)
  2. Complete Phase 2: Foundational (backend DELETE endpoint)
  3. Complete Phase 3: User Story 1 (cancel from detail view)
  4. STOP and VALIDATE: Test cancel flow end-to-end
  5. Deploy/demo if ready

Incremental Delivery

  1. Setup + Foundational → Backend ready
  2. Add US1 → Cancel from detail view works → Deploy (MVP!)
  3. Add US2 → Auto-cancel on list removal → Deploy
  4. Add US3 → Stale token edge cases handled → Deploy
  5. Each story adds value without breaking previous stories

Notes

  • [P] tasks = different files, no dependencies
  • [Story] label maps task to specific user story for traceability
  • Each user story should be independently completable and testable
  • Verify tests fail before implementing
  • Commit after each task or logical group
  • Stop at any checkpoint to validate story independently
  • Backend idempotent DELETE (204 always) simplifies all frontend error handling
  • The @Transactional annotation is required on the service method calling JPA deleteBy...