Files
fete/specs/006-create-event/plan-post-review-fixes.md
nitrix 6aeb4b8bca Migrate project artifacts to spec-kit format
- Move cross-cutting docs (personas, design system, implementation phases,
  Ideen.md) to .specify/memory/
- Move cross-cutting research and plans to .specify/memory/research/ and
  .specify/memory/plans/
- Extract 5 setup tasks from spec/setup-tasks.md into individual
  specs/001-005/spec.md files with spec-kit template format
- Extract 20 user stories from spec/userstories.md into individual
  specs/006-026/spec.md files with spec-kit template format
- Relocate feature-specific research and plan docs into specs/[feature]/
- Add spec-kit constitution, templates, scripts, and slash commands
- Slim down CLAUDE.md to Claude-Code-specific config, delegate principles
  to .specify/memory/constitution.md
- Update ralph.sh with stream-json output and per-iteration logging
- Delete old spec/ and docs/agents/ directories
- Gitignore Ralph iteration JSONL logs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:19:41 +01:00

8.4 KiB

US-1 Post-Review Fixes — Implementation Plan

Date: 2026-03-05 Origin: Deep review of all unstaged US-1 changes before commit

Context

US-1 "Create Event" is fully implemented (backend + frontend, 7 phases) with 4 review fixes already applied (reactive error clearing, network error handling, page title, favicon). A comprehensive review of ALL unstaged files revealed additional issues that must be fixed before committing.

Task 1: Backend — Clock injection in EventService [x]

Problem: EventService uses LocalDate.now() and OffsetDateTime.now() directly, making deterministic time-based testing impossible.

Files:

  • backend/src/main/java/de/fete/application/service/EventService.java
  • backend/src/test/java/de/fete/application/service/EventServiceTest.java

Fix:

  1. Inject a java.time.Clock bean into EventService via constructor
  2. Replace LocalDate.now() with LocalDate.now(clock) and OffsetDateTime.now() with OffsetDateTime.now(clock)
  3. Add a Clock bean to the Spring config (or rely on a @Bean Clock clock() { return Clock.systemDefaultZone(); } in a config class)
  4. Update EventServiceTest to use Clock.fixed(...) for deterministic tests

Verification: cd backend && ./mvnw test

Task 2: Frontend A11y — Error spans should only render when error present [x]

Problem: Every form field has <span class="field-error" role="alert">{{ errors.title }}</span> that is always in the DOM, even when empty. Screen readers may announce empty role="alert" elements.

File: frontend/src/views/EventCreateView.vue

Fix: Use v-if to conditionally render error spans:

<span v-if="errors.title" class="field-error" role="alert">{{ errors.title }}</span>

Apply to all 5 field error spans (title, description, dateTime, location, expiryDate).

Note: This removes the min-height: 1.2em layout reservation. Accept the layout shift as a trade-off for accessibility, OR add a wrapper div with min-height that doesn't carry role="alert".

Verification: cd frontend && npm run test:unit — existing tests use .querySelector('[role="alert"]') so they may need adjustment since empty alerts will no longer be in the DOM.

Task 3: Frontend A11y — aria-invalid and aria-describedby on fields [x]

Problem: When a field fails validation, there is no aria-invalid="true" or aria-describedby linking the input to its error message. Assistive technologies cannot associate errors with fields.

File: frontend/src/views/EventCreateView.vue

Fix:

  1. Add unique id to each error span (e.g., id="title-error")
  2. Add :aria-describedby="errors.title ? 'title-error' : undefined" to each input
  3. Add :aria-invalid="!!errors.title" to each input

Example for title:

<input
  id="title"
  v-model="form.title"
  type="text"
  class="form-field"
  required
  maxlength="200"
  placeholder="What's the event?"
  :aria-invalid="!!errors.title"
  :aria-describedby="errors.title ? 'title-error' : undefined"
/>
<span v-if="errors.title" id="title-error" class="field-error" role="alert">{{ errors.title }}</span>

Apply the same pattern to all 5 fields (title, description, dateTime, location, expiryDate).

Verification: cd frontend && npm run test:unit

Task 4: Frontend A11y — Error text contrast [x]

Problem: White (#fff) error text on the pink gradient start (#F06292) has a contrast ratio of only 3.06:1, which fails WCAG AA for small text (0.8rem). The project statute requires WCAG AA compliance.

File: frontend/src/assets/main.css

Fix options (pick one):

  • Option A: Use a light yellow/cream color like #FFF9C4 or #FFECB3 that has higher contrast on the gradient
  • Option B: Add a subtle dark text-shadow to the error text: text-shadow: 0 1px 2px rgba(0,0,0,0.3)
  • Option C: Make error text slightly larger/bolder to qualify for WCAG AA-large (18px+ or 14px+ bold)

Recommended: Option C — bump .field-error to font-size: 0.85rem; font-weight: 600; which at 600 weight qualifies for AA-large text at 14px+ (0.85rem ≈ 13.6px — close but may not quite qualify). Alternatively combine with option B for safety.

Note: Verify the final choice against the design system spec in spec/design-system.md. The spec notes that gradient start only passes AA-large. The error text must work across the full gradient.

Verification: Manual contrast check with a tool like WebAIM contrast checker.

Task 5: Test — Happy-path submission in EventCreateView [x]

Problem: No test verifies successful form submission (the most important behavior).

File: frontend/src/views/__tests__/EventCreateView.spec.ts

Fix: Add a test that:

  1. Mocks api.POST to return { data: { eventToken: 'abc', organizerToken: 'xyz', title: 'Test', dateTime: '...', expiryDate: '...' } }
  2. Fills all required fields
  3. Submits the form
  4. Asserts api.POST was called with the correct body
  5. Asserts navigation to /events/abc occurred
  6. Asserts saveCreatedEvent was called (need to mock useEventStorage)

Note: useEventStorage must be mocked. Use vi.mock('@/composables/useEventStorage').

Verification: cd frontend && npm run test:unit

Task 6: Test — EventStubView component tests [x]

Problem: No test file exists for EventStubView.vue.

New file: frontend/src/views/__tests__/EventStubView.spec.ts

Fix: Create tests covering:

  1. Renders the event URL based on route param :token
  2. Shows the correct share URL (window.location.origin + /events/:token)
  3. Copy button exists
  4. Back link navigates to home

Note: Read frontend/src/views/EventStubView.vue first to understand the component structure.

Verification: cd frontend && npm run test:unit

Task 7: Test — Server-side field errors in EventCreateView [x]

Problem: The fieldErrors handling branch (lines 184-196 of EventCreateView.vue) is untested.

File: frontend/src/views/__tests__/EventCreateView.spec.ts

Fix: Add a test that:

  1. Mocks api.POST to return { error: { fieldErrors: [{ field: 'title', message: 'Title already taken' }] } }
  2. Fills all required fields and submits
  3. Asserts the title field error shows "Title already taken"
  4. Asserts other field errors are empty

Verification: cd frontend && npm run test:unit

Task 8: Fix border-radius on EventStubView copy button [x]

Problem: border-radius: 10px is hardcoded instead of using the design token var(--radius-button) (14px).

File: frontend/src/views/EventStubView.vue

Fix: Replace border-radius: 10px with border-radius: var(--radius-button) in the .stub__copy CSS class.

Verification: Visual check.

Task 9: Add 404 catch-all route user story [x]

Problem: Navigating to an unknown path shows a blank page.

File: spec/userstories.md

Fix: Add a new user story for a 404/catch-all route. Something like:

### US-X: 404 Page

As a user who navigates to a non-existent URL, I want to see a helpful error page so I can find my way back.

Acceptance Criteria:
- [ ] Unknown routes show a "Page not found" message
- [ ] The page includes a link back to the home page
- [ ] The page follows the design system

Read the existing user stories first to match the format.

Verification: N/A (spec only).

Task 10: EventStubView silent clipboard failure [x]

Problem: In EventStubView.vue, the catch block on navigator.clipboard.writeText() is empty. If clipboard is unavailable (HTTP, older browser), the user gets no feedback.

File: frontend/src/views/EventStubView.vue

Fix: In the catch block, show a fallback message (e.g., set copied text to "Copy failed" or select the URL text for manual copying).

Verification: cd frontend && npm run test:unit

Execution Order

  1. Task 1 (Clock injection — backend, independent)
  2. Tasks 2 + 3 (A11y fixes — can be done together since they touch the same file)
  3. Task 4 (Contrast fix — CSS only)
  4. Tasks 5 + 7 (EventCreateView tests — same test file)
  5. Task 6 (EventStubView tests — new file)
  6. Tasks 8 + 10 (EventStubView fixes — same file)
  7. Task 9 (User story — spec only)
  8. Run all tests: cd backend && ./mvnw test and cd frontend && npm run test:unit

Constraints

  • TDD: write/update tests first, then fix (where applicable)
  • Follow existing code style and patterns
  • Do not refactor unrelated code
  • Do not add dependencies
  • Update design system spec if contrast solution changes the spec