- 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>
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.javabackend/src/test/java/de/fete/application/service/EventServiceTest.java
Fix:
- Inject a
java.time.Clockbean intoEventServicevia constructor - Replace
LocalDate.now()withLocalDate.now(clock)andOffsetDateTime.now()withOffsetDateTime.now(clock) - Add a
Clockbean to the Spring config (or rely on a@Bean Clock clock() { return Clock.systemDefaultZone(); }in a config class) - Update
EventServiceTestto useClock.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:
- Add unique
idto each error span (e.g.,id="title-error") - Add
:aria-describedby="errors.title ? 'title-error' : undefined"to each input - 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
#FFF9C4or#FFECB3that 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:
- Mocks
api.POSTto return{ data: { eventToken: 'abc', organizerToken: 'xyz', title: 'Test', dateTime: '...', expiryDate: '...' } } - Fills all required fields
- Submits the form
- Asserts
api.POSTwas called with the correct body - Asserts navigation to
/events/abcoccurred - Asserts
saveCreatedEventwas called (need to mockuseEventStorage)
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:
- Renders the event URL based on route param
:token - Shows the correct share URL (
window.location.origin + /events/:token) - Copy button exists
- 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:
- Mocks
api.POSTto return{ error: { fieldErrors: [{ field: 'title', message: 'Title already taken' }] } } - Fills all required fields and submits
- Asserts the title field error shows "Title already taken"
- 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
- Task 1 (Clock injection — backend, independent)
- Tasks 2 + 3 (A11y fixes — can be done together since they touch the same file)
- Task 4 (Contrast fix — CSS only)
- Tasks 5 + 7 (EventCreateView tests — same test file)
- Task 6 (EventStubView tests — new file)
- Tasks 8 + 10 (EventStubView fixes — same file)
- Task 9 (User story — spec only)
- Run all tests:
cd backend && ./mvnw testandcd 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