Files
nitrix 763811fce6
All checks were successful
CI / backend-test (push) Successful in 59s
CI / frontend-test (push) Successful in 23s
CI / frontend-e2e (push) Successful in 1m11s
CI / build-and-publish (push) Has been skipped
Add organizer-only attendee list to event detail view (011)
New GET /events/{token}/attendees endpoint returns attendee names when
a valid organizer token is provided (403 otherwise). The frontend
conditionally renders the list below the attendee count for organizers,
silently degrading for visitors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:34:27 +01:00

8.3 KiB

Tasks: View Attendee List

Input: Design documents from /specs/011-view-attendee-list/ Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/api.md

Tests: Included — TDD enforced per constitution principle II. E2E tests mandatory per plan.

Organization: Tasks grouped by user story for independent implementation and testing.

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)
  • Exact file paths included in all descriptions

Phase 1: Setup (API Contract)

Purpose: Define the API contract in OpenAPI and generate types for both backend and frontend.

  • T001 Add GET /events/{token}/attendees endpoint, GetAttendeesResponse, and Attendee schemas to backend/src/main/resources/openapi/api.yaml per contracts/api.md
  • T002 Regenerate backend Spring interfaces (cd backend && ./mvnw compile)
  • T003 Regenerate frontend TypeScript types (cd frontend && npm run generate:api)

Checkpoint: OpenAPI spec updated, generated types available in both backend and frontend.


Phase 2: Foundational (Backend Domain Ports)

Purpose: Create the inbound port and extend the outbound port that all backend implementation depends on.

  • T004 [P] Create GetAttendeesUseCase inbound port interface in backend/src/main/java/de/fete/domain/port/in/GetAttendeesUseCase.java with method List<String> getAttendeeNames(UUID eventToken, UUID organizerToken)
  • T005 [P] Add List<Rsvp> findByEventId(Long eventId) method to backend/src/main/java/de/fete/domain/port/out/RsvpRepository.java

Checkpoint: Domain ports defined — service and adapter implementation can begin.


Phase 3: User Story 1 — View Attendee List as Organizer (Priority: P1) 🎯 MVP

Goal: Organizer sees a list of attendee names on the event detail page. Non-organizers see only the count (existing behavior).

Independent Test: Create an event, submit RSVPs, then view the detail page with the organizer token. Attendee names should be listed.

Tests for User Story 1 ⚠️

Write these tests FIRST — ensure they FAIL before implementation.

  • T006 [P] [US1] Write unit tests for RsvpService.getAttendeeNames (valid token, invalid token, event not found, no RSVPs) in backend/src/test/java/de/fete/application/service/RsvpServiceTest.java
  • T007 [P] [US1] Write integration tests for GET /events/{token}/attendees (200 with attendees, 200 empty, 403 invalid token, 404 event not found) in backend/src/test/java/de/fete/adapter/in/web/EventControllerIntegrationTest.java
  • T008 [P] [US1] Write unit tests for AttendeeList.vue (renders attendee names, empty state message, loading state) in frontend/src/components/__tests__/AttendeeList.spec.ts
  • T009 [P] [US1] Write unit tests for organizer-conditional rendering in EventDetailView.vue (shows list for organizer, hides for visitor) in frontend/src/views/__tests__/EventDetailView.spec.ts
  • T010 [P] [US1] Write E2E test: organizer sees attendee names, visitor sees count only, in frontend/e2e/view-attendee-list.spec.ts

Backend Implementation for User Story 1

  • T011 [P] [US1] Add findAllByEventIdOrderByIdAsc query method to backend/src/main/java/de/fete/adapter/out/persistence/RsvpJpaRepository.java
  • T012 [P] [US1] Implement findByEventId in backend/src/main/java/de/fete/adapter/out/persistence/RsvpPersistenceAdapter.java
  • T013 [US1] Implement GetAttendeesUseCase in backend/src/main/java/de/fete/application/service/RsvpService.java — look up event by token, verify organizer token, return attendee names ordered by ID
  • T014 [US1] Add getAttendees endpoint handler to backend/src/main/java/de/fete/adapter/in/web/EventController.java — map to GetAttendeesUseCase, return 200/403/404

Frontend Implementation for User Story 1

  • T015 [US1] Create AttendeeList.vue component in frontend/src/components/AttendeeList.vue — accepts attendee names array as prop, renders semantic <ul>/<li> list, shows empty state message when no attendees
  • T016 [US1] Integrate AttendeeList.vue into frontend/src/views/EventDetailView.vue — fetch GET /events/{token}/attendees with organizer token from localStorage, render below attendee count (before RSVP form), silently degrade on 403

Checkpoint: User Story 1 fully functional. Organizer sees attendee names; visitor sees count only. All tests pass.


Phase 4: User Story 2 — Attendee Count Label (Priority: P2)

Goal: The attendee list section shows a count label ("5 Attendees" / "1 Attendee") alongside the names.

Independent Test: Verify the count label above the list matches the number of entries, and uses singular/plural correctly.

Tests for User Story 2 ⚠️

Write these tests FIRST — ensure they FAIL before implementation.

  • T017 [P] [US2] Write unit tests for count label in AttendeeList.vue (plural "5 Attendees", singular "1 Attendee", zero "0 Attendees") in frontend/src/components/__tests__/AttendeeList.spec.ts

Implementation for User Story 2

  • T018 [US2] Add count heading to AttendeeList.vue in frontend/src/components/AttendeeList.vue — render <h3> with singular/plural label based on attendee array length

Checkpoint: User Story 2 complete. Count label renders correctly with singular/plural form. All tests pass.


Phase 5: Polish & Cross-Cutting Concerns

Purpose: Verification across both stories, accessibility, edge cases.

  • T019 Run full backend verification (cd backend && ./mvnw verify) — checkstyle, all tests green
  • T020 Run full frontend build and tests (cd frontend && npm run build && npm run test:unit)
  • T021 Run E2E tests (cd frontend && npx playwright test e2e/view-attendee-list.spec.ts)
  • T022 Verify WCAG AA contrast and semantic HTML (attendee list uses <ul>/<li>, section has heading for screen readers)
  • T023 Verify edge cases: long names truncated visually, special characters escaped, large attendee list scrollable

Dependencies & Execution Order

Phase Dependencies

  • Setup (Phase 1): No dependencies — start immediately
  • Foundational (Phase 2): Depends on T002 (generated backend interfaces)
  • User Story 1 (Phase 3): Depends on Phase 2 completion
  • User Story 2 (Phase 4): Depends on T015 (AttendeeList component exists)
  • Polish (Phase 5): Depends on Phase 3 + Phase 4 completion

User Story Dependencies

  • User Story 1 (P1): Can start after Phase 2 — no dependencies on other stories
  • User Story 2 (P2): Depends on US1's AttendeeList.vue component (T015) existing

Within Each User Story

  • Tests MUST be written and FAIL before implementation
  • Ports/models before services
  • Services before endpoints/controllers
  • Backend before frontend (API must exist for frontend integration)

Parallel Opportunities

  • T004 + T005 can run in parallel (different files)
  • T006 + T007 + T008 + T009 + T010 can all run in parallel (different test files)
  • T011 + T012 can run in parallel (different persistence files)

Parallel Example: User Story 1

# Launch all tests in parallel (TDD — write first):
Task: T006 "Unit tests for RsvpService.getAttendeeNames"
Task: T007 "Integration tests for GET /events/{token}/attendees"
Task: T008 "Unit tests for AttendeeList.vue"
Task: T009 "Unit tests for EventDetailView.vue organizer rendering"
Task: T010 "E2E test for attendee list"

# Launch persistence layer in parallel:
Task: T011 "Add findAllByEventIdOrderByIdAsc to RsvpJpaRepository"
Task: T012 "Implement findByEventId in RsvpPersistenceAdapter"

Implementation Strategy

MVP First (User Story 1 Only)

  1. Complete Phase 1: Setup (OpenAPI + type generation)
  2. Complete Phase 2: Foundational (domain ports)
  3. Complete Phase 3: User Story 1 (backend + frontend)
  4. STOP and VALIDATE: Test independently — organizer sees names, visitor sees count only
  5. Deploy/demo if ready

Incremental Delivery

  1. Setup + Foundational → API contract and ports ready
  2. Add User Story 1 → Test independently → Deploy (MVP!)
  3. Add User Story 2 → Test independently → Deploy (count label enhancement)
  4. Polish phase → Full verification, accessibility, edge cases