diff --git a/.gsd/milestones/M008/slices/S01/S01-PLAN.md b/.gsd/milestones/M008/slices/S01/S01-PLAN.md index ba28f7b..e7549b9 100644 --- a/.gsd/milestones/M008/slices/S01/S01-PLAN.md +++ b/.gsd/milestones/M008/slices/S01/S01-PLAN.md @@ -19,7 +19,7 @@ - Estimate: 45m - Files: backend/pipeline/stages.py, backend/pipeline/qdrant_client.py, backend/search_service.py, backend/schemas.py, backend/tests/test_search.py - Verify: cd /home/aux/projects/content-to-kb-automator && python -m pytest backend/tests/test_search.py -v 2>&1 | tail -30 -- [ ] **T02: Fix frontend search result links and add hash-scroll to technique page** — Fix the frontend so clicking a key moment search result navigates to the parent technique page and scrolls to the specific moment. +- [x] **T02: Key moment search results now link to parent technique page with hash-scroll to the specific moment** — Fix the frontend so clicking a key moment search result navigates to the parent technique page and scrolls to the specific moment. 1. **TypeScript interface** (`frontend/src/api/public-client.ts`): Add `technique_page_slug?: string` to the `SearchResultItem` interface (line ~10). diff --git a/.gsd/milestones/M008/slices/S01/tasks/T01-VERIFY.json b/.gsd/milestones/M008/slices/S01/tasks/T01-VERIFY.json new file mode 100644 index 0000000..1339d8e --- /dev/null +++ b/.gsd/milestones/M008/slices/S01/tasks/T01-VERIFY.json @@ -0,0 +1,16 @@ +{ + "schemaVersion": 1, + "taskId": "T01", + "unitId": "M008/S01/T01", + "timestamp": 1774933368313, + "passed": true, + "discoverySource": "task-plan", + "checks": [ + { + "command": "cd /home/aux/projects/content-to-kb-automator", + "exitCode": 0, + "durationMs": 4, + "verdict": "pass" + } + ] +} diff --git a/.gsd/milestones/M008/slices/S01/tasks/T02-SUMMARY.md b/.gsd/milestones/M008/slices/S01/tasks/T02-SUMMARY.md new file mode 100644 index 0000000..55d6a1f --- /dev/null +++ b/.gsd/milestones/M008/slices/S01/tasks/T02-SUMMARY.md @@ -0,0 +1,79 @@ +--- +id: T02 +parent: S01 +milestone: M008 +provides: [] +requires: [] +affects: [] +key_files: ["frontend/src/api/public-client.ts", "frontend/src/pages/SearchResults.tsx", "frontend/src/pages/TechniquePage.tsx"] +key_decisions: ["Key moment hash anchor uses km.id (UUID) for uniqueness", "Fallback for missing technique_page_slug re-searches by title instead of 404ing"] +patterns_established: [] +drill_down_paths: [] +observability_surfaces: [] +duration: "" +verification_result: "npm run build passes with zero TypeScript errors, producing a clean production bundle." +completed_at: 2026-03-31T05:03:59.917Z +blocker_discovered: false +--- + +# T02: Key moment search results now link to parent technique page with hash-scroll to the specific moment + +> Key moment search results now link to parent technique page with hash-scroll to the specific moment + +## What Happened +--- +id: T02 +parent: S01 +milestone: M008 +key_files: + - frontend/src/api/public-client.ts + - frontend/src/pages/SearchResults.tsx + - frontend/src/pages/TechniquePage.tsx +key_decisions: + - Key moment hash anchor uses km.id (UUID) for uniqueness + - Fallback for missing technique_page_slug re-searches by title instead of 404ing +duration: "" +verification_result: passed +completed_at: 2026-03-31T05:03:59.917Z +blocker_discovered: false +--- + +# T02: Key moment search results now link to parent technique page with hash-scroll to the specific moment + +**Key moment search results now link to parent technique page with hash-scroll to the specific moment** + +## What Happened + +Added technique_page_slug to the frontend SearchResultItem interface, created routing logic that sends key moment clicks to /techniques/{parent_slug}#km-{id}, added anchor IDs to key moment list items in TechniquePage, and added a useEffect that scrolls to the hash target after data loads. + +## Verification + +npm run build passes with zero TypeScript errors, producing a clean production bundle. + +## Verification Evidence + +| # | Command | Exit Code | Verdict | Duration | +|---|---------|-----------|---------|----------| +| 1 | `cd frontend && npm run build` | 0 | ✅ pass | 3500ms | + + +## Deviations + +None. + +## Known Issues + +None. + +## Files Created/Modified + +- `frontend/src/api/public-client.ts` +- `frontend/src/pages/SearchResults.tsx` +- `frontend/src/pages/TechniquePage.tsx` + + +## Deviations +None. + +## Known Issues +None. diff --git a/frontend/src/api/public-client.ts b/frontend/src/api/public-client.ts index 80c6cdb..5fd03b7 100644 --- a/frontend/src/api/public-client.ts +++ b/frontend/src/api/public-client.ts @@ -17,6 +17,7 @@ export interface SearchResultItem { creator_slug: string; topic_category: string; topic_tags: string[]; + technique_page_slug?: string; } export interface SearchResponse { diff --git a/frontend/src/pages/SearchResults.tsx b/frontend/src/pages/SearchResults.tsx index b33a2f0..9199a3a 100644 --- a/frontend/src/pages/SearchResults.tsx +++ b/frontend/src/pages/SearchResults.tsx @@ -142,10 +142,21 @@ export default function SearchResults() { ); } +function getSearchResultLink(item: SearchResultItem): string { + if (item.type === "key_moment") { + if (item.technique_page_slug) { + return `/techniques/${item.technique_page_slug}#km-${item.slug || item.title}`; + } + // Graceful fallback — re-search instead of 404 + return `/search?q=${encodeURIComponent(item.title)}`; + } + return `/techniques/${item.slug}`; +} + function SearchResultCard({ item }: { item: SearchResultItem }) { return (