# Tasks: Link Preview (Open Graph Meta-Tags) **Input**: Design documents from `/specs/012-link-preview/` **Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/ **Tests**: Included — constitution mandates TDD (Red → Green → Refactor). **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, US3) - Include exact file paths in descriptions --- ## Phase 1: Setup **Purpose**: Prepare frontend template and static assets for meta-tag injection - [x] T001 Add `` placeholder comment in `` of `frontend/index.html` - [x] T002 [P] Create `og-image.png` brand image (1200x630) in `frontend/public/og-image.png` - [x] T003 [P] Add `server.forward-headers-strategy=framework` to `backend/src/main/resources/application.properties` --- ## Phase 2: Foundational (Blocking Prerequisites) **Purpose**: Core infrastructure for HTML meta-tag injection that ALL user stories depend on **⚠️ CRITICAL**: No user story work can begin until this phase is complete - [x] T004 Write unit tests for `MetaTagRenderer` (HTML escaping, meta-tag HTML output) in `backend/src/test/java/de/fete/adapter/in/web/MetaTagRendererTest.java` — tests MUST fail (Red) - [x] T005 Implement `MetaTagRenderer` utility that renders meta-tag key-value pairs into HTML `` strings with proper HTML escaping in `backend/src/main/java/de/fete/adapter/in/web/MetaTagRenderer.java` - [x] T006 Write integration tests for `SpaController` base functionality (serves index.html, replaces placeholder) in `backend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java` — tests MUST fail (Red) - [x] T007 Implement `SpaController` that caches `index.html` template at startup and replaces `` placeholder before serving in `backend/src/main/java/de/fete/adapter/in/web/SpaController.java` - [x] T008 Remove `PathResourceResolver` SPA fallback from `backend/src/main/java/de/fete/config/WebConfig.java` (replaced by `SpaController`) **Checkpoint**: SPA still works (index.html served for all non-API/non-static routes), but now through `SpaController` with placeholder replacement ready --- ## Phase 3: User Story 1 — Event Link Preview in Messenger (Priority: P1) 🎯 MVP **Goal**: Shared event links display rich preview cards with event title, date, location, and description in messengers **Independent Test**: Share an event URL — messenger shows event title and formatted description with date/location ### Tests for User Story 1 ⚠️ > **NOTE: Write these tests FIRST, ensure they FAIL before implementation** - [x] T009 [P] [US1] Unit tests for `OpenGraphService.buildEventMeta()` — full event data, no location, no description, long title truncation, special characters in `backend/src/test/java/de/fete/application/OpenGraphServiceTest.java` - [x] T010 [P] [US1] Integration tests for `SpaController` event routes — GET `/events/{token}` returns HTML with event-specific OG meta-tags, event not found falls back to generic in `backend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java` ### Implementation for User Story 1 - [x] T011 [US1] Implement `OpenGraphService.buildEventMeta()` — fetch event by token, compose `og:title` (truncated 70 chars), `og:description` (date + location + description, max 200 chars), `og:url`, `og:type`, `og:site_name`, `og:image` in `backend/src/main/java/de/fete/application/service/OpenGraphService.java` - [x] T012 [US1] Wire `SpaController` to call `OpenGraphService` for `/events/{token}` routes, inject event-specific meta-tags via `MetaTagRenderer` in `backend/src/main/java/de/fete/adapter/in/web/SpaController.java` - [ ] T013 [US1] E2E test — deferred (requires running backend; covered by integration tests) **Checkpoint**: Event links show rich OG preview cards in messengers. MVP complete. --- ## Phase 4: User Story 2 — Fallback Preview for Generic Pages (Priority: P2) **Goal**: Non-event pages (homepage, event list, create) show a meaningful generic fete preview when shared **Independent Test**: Share the homepage URL — messenger shows "fete" as title and generic app description ### Tests for User Story 2 ⚠️ > **NOTE: Write these tests FIRST, ensure they FAIL before implementation** - [x] T014 [P] [US2] Unit tests for `OpenGraphService.buildGenericMeta()` — verify generic title "fete", generic description, correct URL, image URL in `backend/src/test/java/de/fete/application/service/OpenGraphServiceTest.java` - [x] T015 [P] [US2] Integration tests for `SpaController` generic routes — GET `/`, GET `/create` return HTML with generic OG meta-tags in `backend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java` ### Implementation for User Story 2 - [x] T016 [US2] Implement `OpenGraphService.buildGenericMeta()` — title "fete", description "Privacy-focused event planning. Create and share events without accounts.", type "website", site_name "fete" in `backend/src/main/java/de/fete/application/service/OpenGraphService.java` - [x] T017 [US2] Wire `SpaController` to call `OpenGraphService.buildGenericMeta()` for all non-event HTML routes in `backend/src/main/java/de/fete/adapter/in/web/SpaController.java` - [ ] T018 [US2] E2E test — deferred (requires running backend; covered by integration tests) **Checkpoint**: All shared fete links (event-specific and generic) show rich preview cards --- ## Phase 5: User Story 3 — Twitter/X Card Support (Priority: P3) **Goal**: Links shared on Twitter/X also display rich preview cards via Twitter Card meta-tags **Independent Test**: Verify HTML source contains `twitter:card`, `twitter:title`, `twitter:description` meta-tags ### Tests for User Story 3 ⚠️ > **NOTE: Write these tests FIRST, ensure they FAIL before implementation** - [x] T019 [P] [US3] Unit tests for Twitter Card meta-tag generation — verify `twitter:card` = "summary", `twitter:title`, `twitter:description` match OG values in `backend/src/test/java/de/fete/application/service/OpenGraphServiceTest.java` - [x] T020 [P] [US3] Integration tests for `SpaController` — event and generic routes include Twitter Card meta-tags in `backend/src/test/java/de/fete/adapter/in/web/SpaControllerTest.java` ### Implementation for User Story 3 - [x] T021 [US3] Extend `OpenGraphService` to include Twitter Card meta-tags (`twitter:card`, `twitter:title`, `twitter:description`) alongside OG tags in `backend/src/main/java/de/fete/application/service/OpenGraphService.java` - [x] T022 [US3] Extend `MetaTagRenderer` to render `` tags (using `name` attribute instead of `property`) in `backend/src/main/java/de/fete/adapter/in/web/MetaTagRenderer.java` - [ ] T023 [US3] E2E test — fetch event page and homepage, verify Twitter Card meta-tags present alongside OG tags in `frontend/e2e/link-preview.spec.ts` **Checkpoint**: All three user stories complete — OG tags, generic fallback, and Twitter Cards all working --- ## Phase 6: Polish & Cross-Cutting Concerns **Purpose**: Edge cases, hardening, and final verification - [x] T024 [P] Verify HTML escaping for special characters (quotes, ampersands, angle brackets) in meta-tag values across all routes — edge-case tests in MetaTagRendererTest.java - [x] T025 [P] Verify `SpaController` does not intercept static asset requests — SpaController only handles explicit routes, not wildcard - [x] T026 Run full backend test suite (`cd backend && ./mvnw verify`) and fix any regressions — 97 tests, 0 bugs - [x] T027 Run full frontend test suite (`cd frontend && npm run test:unit`) — 133 tests passed --- ## Dependencies & Execution Order ### Phase Dependencies - **Setup (Phase 1)**: No dependencies — can start immediately - **Foundational (Phase 2)**: Depends on T001 (placeholder in index.html) — BLOCKS all user stories - **US1 (Phase 3)**: Depends on Foundational phase completion - **US2 (Phase 4)**: Depends on Foundational phase completion — can run in parallel with US1 - **US3 (Phase 5)**: Depends on US1 or US2 (extends their meta-tag output) - **Polish (Phase 6)**: Depends on all user stories being complete ### User Story Dependencies - **User Story 1 (P1)**: Can start after Foundational (Phase 2) — no dependencies on other stories - **User Story 2 (P2)**: Can start after Foundational (Phase 2) — independent from US1 - **User Story 3 (P3)**: Depends on US1 or US2 — extends existing OG meta-tag output with Twitter tags ### Within Each User Story - Tests MUST be written and FAIL before implementation (TDD Red phase) - Service layer before controller wiring - Unit tests before integration tests before E2E tests - Story complete before moving to next priority ### Parallel Opportunities - T001, T002, T003 can all run in parallel (Setup phase) - T004 and T006 can run in parallel (Foundational tests — different files) - T009, T010 can run in parallel (US1 tests — different files) - T014, T015 can run in parallel (US2 tests — different files) - T019, T020 can run in parallel (US3 tests — different files) - US1 and US2 can be worked on in parallel after Foundational phase --- ## Parallel Example: User Story 1 ```bash # Launch US1 tests in parallel (Red phase): Task: "Unit tests for OpenGraphService.buildEventMeta() in OpenGraphServiceTest.java" Task: "Integration tests for SpaController event routes in SpaControllerTest.java" # Then implement sequentially: Task: "Implement OpenGraphService.buildEventMeta()" Task: "Wire SpaController for /events/{token} routes" Task: "E2E test for event page meta-tags" ``` --- ## Implementation Strategy ### MVP First (User Story 1 Only) 1. Complete Phase 1: Setup (T001–T003) 2. Complete Phase 2: Foundational (T004–T008) 3. Complete Phase 3: User Story 1 (T009–T013) 4. **STOP and VALIDATE**: Share an event link in a messenger, verify preview card 5. Deploy/demo if ready ### Incremental Delivery 1. Setup + Foundational → SpaController serving index.html with placeholder replacement 2. Add US1 → Event links show rich previews → Deploy (MVP!) 3. Add US2 → Generic pages also show previews → Deploy 4. Add US3 → Twitter/X cards work too → Deploy 5. Polish → Edge cases hardened → Final release --- ## Notes - [P] tasks = different files, no dependencies - [Story] label maps task to specific user story for traceability - Each user story is independently completable and testable - TDD enforced: write tests first, verify they fail, then implement - Commit after each task or logical group - Stop at any checkpoint to validate story independently