10 KiB
Tasks: Cancel Event
Input: Design documents from /specs/016-cancel-event/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/patch-event-endpoint.yaml
Tests: Included — constitution mandates TDD (tests before implementation) and E2E for every frontend user story.
Organization: Tasks grouped by user story. Both stories are P1 but US2 depends on US1's backend work.
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)
- Include exact file paths in descriptions
Phase 1: Setup (Shared Infrastructure)
Purpose: OpenAPI spec, database migration, and domain model changes that both user stories depend on.
- T001 Update OpenAPI spec with PATCH endpoint on
/events/{eventToken}, PatchEventRequest schema (organizerToken,cancelled,cancellationReason), and extend GetEventResponse withcancelled/cancellationReasonfields inbackend/src/main/resources/openapi/api.yaml - T002 Add Liquibase changeset 004 adding
cancelled(BOOLEAN NOT NULL DEFAULT FALSE) andcancellation_reason(VARCHAR 2000) columns toeventstable inbackend/src/main/resources/db/changelog/004-add-cancellation-columns.xmland register it indb.changelog-master.xml - T003 [P] Extend domain model
Event.javawithcancelled,cancellationReasonfields andcancel(String reason)method (throwsEventAlreadyCancelledException). CreateEventAlreadyCancelledExceptioninbackend/src/main/java/de/fete/application/service/following the pattern of existing exceptions. Domain model:backend/src/main/java/de/fete/domain/model/Event.java - T004 [P] Extend JPA entity
EventJpaEntity.javawithcancelledandcancellation_reasoncolumns and update mapper inbackend/src/main/java/de/fete/adapter/out/persistence/EventJpaEntity.java - T005 Regenerate frontend TypeScript types from updated OpenAPI spec via
cd frontend && npm run generate-types(or equivalent openapi-typescript command)
Checkpoint: Schema, migration, and domain model ready. Both user stories can now proceed.
Phase 2: User Story 1 — Organizer Cancels an Event (Priority: P1) MVP
Goal: Organizer can cancel an event via a bottom sheet with optional reason. Cancellation is irreversible and persists on reload.
Independent Test: View event with organizer token, tap cancel, optionally enter reason, confirm. Event remains cancelled on reload.
Tests for User Story 1
Write these tests FIRST — ensure they FAIL before implementation
- T006 [P] [US1] Write unit test for
cancel()domain method (happy path, already-cancelled throws) inbackend/src/test/java/de/fete/domain/model/EventTest.java - T007 [P] [US1] Write unit test for cancel use case in EventService (delegates to domain, saves, 403/404/409 cases) in
backend/src/test/java/de/fete/application/service/EventServiceCancelTest.java - T008 [P] [US1] Write integration test for
PATCH /events/{eventToken}endpoint (204 success, 403 wrong token, 404 not found, 409 already cancelled) inbackend/src/test/java/de/fete/adapter/in/web/EventControllerCancelTest.java - T009 [P] [US1] Write E2E test: organizer opens cancel bottom sheet, enters reason, confirms — event shows as cancelled on reload in
frontend/e2e/cancel-event.spec.ts - T010 [P] [US1] Write E2E test: organizer cancels without reason — event shows as cancelled in
frontend/e2e/cancel-event.spec.ts - T011 [P] [US1] Write E2E test: cancel API fails — error displayed in bottom sheet, button re-enabled for retry in
frontend/e2e/cancel-event.spec.ts
Implementation for User Story 1
- T012 [US1] Create
CancelEventUseCaseinterface (or add method to existing use case interface) inbackend/src/main/java/de/fete/domain/port/in/ - T013 [US1] Implement cancel logic in
EventService.java— load event, verify organizer token, callevent.cancel(reason), persist inbackend/src/main/java/de/fete/application/service/EventService.java - T014 [US1] Implement
cancelEventendpoint inEventController.java— PATCH handler, request body binding, error mapping (403/404/409) inbackend/src/main/java/de/fete/adapter/in/web/EventController.java - T015 [US1] Add cancel button (visible only when organizer token exists and event not cancelled — covers FR-012) and cancel bottom sheet (textarea with 2000 char limit + confirm button + inline error) to
frontend/src/views/EventDetailView.vue - T016 [US1] Wire cancel bottom sheet confirm action to
PATCH /events/{eventToken}API call via openapi-fetch, handle success (reload event data) and error (show inline message, re-enable button) infrontend/src/views/EventDetailView.vue
Checkpoint: Organizer can cancel an event. All US1 acceptance scenarios pass.
Phase 3: User Story 2 — Attendee Sees Cancelled Event (Priority: P1)
Goal: Visitors see a prominent red cancellation banner on cancelled events. RSVP buttons are hidden. All other event details remain visible.
Independent Test: View a cancelled event's detail page — banner visible (with reason if provided), RSVP buttons hidden, other details intact.
Tests for User Story 2
Write these tests FIRST — ensure they FAIL before implementation
- T017 [P] [US2] Write unit test for RSVP creation rejection on cancelled events (409 Conflict) in
backend/src/test/java/de/fete/application/service/RsvpServiceCancelledTest.java - T018 [P] [US2] Write integration test for
POST /events/{eventToken}/rsvpsreturning 409 when event is cancelled inbackend/src/test/java/de/fete/adapter/in/web/RsvpControllerCancelledTest.java - T019 [P] [US2] Write E2E test: visitor sees red banner with cancellation reason on cancelled event in
frontend/e2e/cancelled-event-visitor.spec.ts - T020 [P] [US2] Write E2E test: visitor sees red banner without reason when no reason was provided in
frontend/e2e/cancelled-event-visitor.spec.ts - T021 [P] [US2] Write E2E test: RSVP buttons hidden on cancelled event, other details remain visible in
frontend/e2e/cancelled-event-visitor.spec.ts
Implementation for User Story 2
- T022 [US2] Add cancelled-event guard to RSVP creation — check
event.isCancelled(), return 409 Conflict inbackend/src/main/java/de/fete/application/service/RsvpService.java - T023 [US2] Add cancellation banner component/section (red, prominent, includes reason if present, WCAG AA contrast) to
frontend/src/views/EventDetailView.vue - T024 [US2] Hide RSVP buttons (
RsvpBaror equivalent) whenevent.cancelled === trueinfrontend/src/views/EventDetailView.vue T025Merged into T015 (cancel button v-if already handles FR-012)
Checkpoint: Both user stories fully functional. All acceptance scenarios pass.
Phase 4: Polish & Cross-Cutting Concerns
Purpose: Validation, edge cases, and final cleanup.
- T026 Verify cancellationReason max length (2000 chars) is enforced at API level (OpenAPI
maxLength), domain level inEvent.java, and UI level (textarea maxlength/counter) - T027 Run full backend test suite (
cd backend && ./mvnw verify) and fix any failures - T028 Run full frontend test suite (
cd frontend && npm run test:unit) and fix any failures - T029 Run E2E tests (
cd frontend && npx playwright test) and fix any failures - T030 Run backend checkstyle (
cd backend && ./mvnw checkstyle:check) and fix violations
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): No dependencies — start immediately
- US1 (Phase 2): Depends on Setup completion
- US2 (Phase 3): Depends on Setup completion; backend RSVP guard is independent of US1, but frontend banner/hiding can be built in parallel with US1
- Polish (Phase 4): Depends on all user stories complete
User Story Dependencies
- US1 (P1): Can start after Phase 1 (Setup). No dependency on US2.
- US2 (P1): Can start after Phase 1 (Setup). Backend RSVP guard is independent of US1. Frontend banner/hiding only needs the
cancelled/cancellationReasonfields in GetEventResponse (from Setup).
Within Each User Story
- Tests MUST be written and FAIL before implementation
- Domain model before service layer
- Service layer before controller/endpoint
- Backend before frontend (API must exist before UI wires to it)
- Core implementation before error handling polish
Parallel Opportunities
- T003 + T004 (domain model + JPA entity) can run in parallel
- All US1 test tasks (T006–T011) can run in parallel
- All US2 test tasks (T017–T021) can run in parallel
- US1 backend (T012–T014) and US2 backend (T022) can run in parallel after Setup
- US1 frontend (T015–T016) and US2 frontend (T023–T025) can run in parallel if backend is ready
Parallel Example: User Story 1
# Launch all US1 tests together (TDD - write first, expect failures):
Task: T006 "Unit test for cancel() domain method"
Task: T007 "Unit test for cancel use case in EventService"
Task: T008 "Integration test for PATCH /events/{eventToken}"
Task: T009 "E2E test: organizer cancels with reason"
Task: T010 "E2E test: organizer cancels without reason"
Task: T011 "E2E test: cancel API failure handling"
# Then implement sequentially:
Task: T012 "CancelEventUseCase interface"
Task: T013 "EventService cancel implementation"
Task: T014 "EventController cancelEvent endpoint"
Task: T015 "Cancel button + bottom sheet in EventDetailView"
Task: T016 "Wire cancel action to API"
Implementation Strategy
MVP First (User Story 1 Only)
- Complete Phase 1: Setup (OpenAPI, migration, domain model)
- Complete Phase 2: US1 — Organizer cancels event
- STOP and VALIDATE: All US1 acceptance scenarios pass
- Deploy/demo if ready — cancellation works end-to-end
Incremental Delivery
- Setup → Shared infrastructure ready
- US1 → Organizer can cancel → Test → Deploy (MVP!)
- US2 → Attendees see cancellation + RSVP blocked → Test → Deploy
- Polish → Full verification, checkstyle, edge cases