From f822415f6f7d9f3f0b8f9b7e1f412106e37efc2f Mon Sep 17 00:00:00 2001 From: jlightner Date: Sat, 4 Apr 2026 06:12:10 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Wired=20ChapterReview=20into=20App=20ro?= =?UTF-8?q?utes=20(/creator/chapters,=20/creator/=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "frontend/src/App.tsx" - "frontend/src/pages/CreatorDashboard.tsx" - "frontend/src/pages/ChapterReview.tsx" - "frontend/src/pages/ChapterReview.module.css" GSD-Task: S06/T03 --- .gsd/milestones/M021/slices/S06/S06-PLAN.md | 2 +- .../M021/slices/S06/tasks/T02-VERIFY.json | 36 ++++++++ .../M021/slices/S06/tasks/T03-SUMMARY.md | 86 +++++++++++++++++++ frontend/src/App.tsx | 1 + frontend/src/pages/ChapterReview.module.css | 48 +++++++++++ frontend/src/pages/ChapterReview.tsx | 75 +++++++++++++++- frontend/src/pages/CreatorDashboard.tsx | 10 +-- 7 files changed, 249 insertions(+), 9 deletions(-) create mode 100644 .gsd/milestones/M021/slices/S06/tasks/T02-VERIFY.json create mode 100644 .gsd/milestones/M021/slices/S06/tasks/T03-SUMMARY.md diff --git a/.gsd/milestones/M021/slices/S06/S06-PLAN.md b/.gsd/milestones/M021/slices/S06/S06-PLAN.md index f59a3bd..67b0321 100644 --- a/.gsd/milestones/M021/slices/S06/S06-PLAN.md +++ b/.gsd/milestones/M021/slices/S06/S06-PLAN.md @@ -48,7 +48,7 @@ Key constraints: - Estimate: 2h - Files: frontend/src/api/videos.ts, frontend/src/pages/ChapterReview.tsx, frontend/src/pages/ChapterReview.module.css - Verify: cd frontend && npx tsc --noEmit 2>&1 | head -30 && test -f src/pages/ChapterReview.tsx && test -f src/pages/ChapterReview.module.css && grep -q 'updateChapter' src/api/videos.ts -- [ ] **T03: Wire Chapter Review into App routes and CreatorDashboard sidebar navigation** — Connect the Chapter Review page to the application routing and navigation. +- [x] **T03: Wired ChapterReview into App routes (/creator/chapters, /creator/chapters/:videoId) with sidebar NavLink and video picker view** — Connect the Chapter Review page to the application routing and navigation. Steps: 1. In `frontend/src/App.tsx`: diff --git a/.gsd/milestones/M021/slices/S06/tasks/T02-VERIFY.json b/.gsd/milestones/M021/slices/S06/tasks/T02-VERIFY.json new file mode 100644 index 0000000..9b30d82 --- /dev/null +++ b/.gsd/milestones/M021/slices/S06/tasks/T02-VERIFY.json @@ -0,0 +1,36 @@ +{ + "schemaVersion": 1, + "taskId": "T02", + "unitId": "M021/S06/T02", + "timestamp": 1775282843990, + "passed": false, + "discoverySource": "task-plan", + "checks": [ + { + "command": "cd frontend", + "exitCode": 0, + "durationMs": 4, + "verdict": "pass" + }, + { + "command": "test -f src/pages/ChapterReview.tsx", + "exitCode": 1, + "durationMs": 4, + "verdict": "fail" + }, + { + "command": "test -f src/pages/ChapterReview.module.css", + "exitCode": 1, + "durationMs": 4, + "verdict": "fail" + }, + { + "command": "grep -q 'updateChapter' src/api/videos.ts", + "exitCode": 2, + "durationMs": 4, + "verdict": "fail" + } + ], + "retryAttempt": 1, + "maxRetries": 2 +} diff --git a/.gsd/milestones/M021/slices/S06/tasks/T03-SUMMARY.md b/.gsd/milestones/M021/slices/S06/tasks/T03-SUMMARY.md new file mode 100644 index 0000000..d76272f --- /dev/null +++ b/.gsd/milestones/M021/slices/S06/tasks/T03-SUMMARY.md @@ -0,0 +1,86 @@ +--- +id: T03 +parent: S06 +milestone: M021 +provides: [] +requires: [] +affects: [] +key_files: ["frontend/src/App.tsx", "frontend/src/pages/CreatorDashboard.tsx", "frontend/src/pages/ChapterReview.tsx", "frontend/src/pages/ChapterReview.module.css"] +key_decisions: ["Video picker uses consent API for creator video listing", "ChapterReview split into wrapper + detail component for clean param handling"] +patterns_established: [] +drill_down_paths: [] +observability_surfaces: [] +duration: "" +verification_result: "TypeScript compilation (npx tsc --noEmit) passes with no errors. All grep/test checks for ChapterReview in App.tsx, /creator/chapters in CreatorDashboard, file existence, and updateChapter in videos.ts pass." +completed_at: 2026-04-04T06:11:59.484Z +blocker_discovered: false +--- + +# T03: Wired ChapterReview into App routes (/creator/chapters, /creator/chapters/:videoId) with sidebar NavLink and video picker view + +> Wired ChapterReview into App routes (/creator/chapters, /creator/chapters/:videoId) with sidebar NavLink and video picker view + +## What Happened +--- +id: T03 +parent: S06 +milestone: M021 +key_files: + - frontend/src/App.tsx + - frontend/src/pages/CreatorDashboard.tsx + - frontend/src/pages/ChapterReview.tsx + - frontend/src/pages/ChapterReview.module.css +key_decisions: + - Video picker uses consent API for creator video listing + - ChapterReview split into wrapper + detail component for clean param handling +duration: "" +verification_result: passed +completed_at: 2026-04-04T06:11:59.484Z +blocker_discovered: false +--- + +# T03: Wired ChapterReview into App routes (/creator/chapters, /creator/chapters/:videoId) with sidebar NavLink and video picker view + +**Wired ChapterReview into App routes (/creator/chapters, /creator/chapters/:videoId) with sidebar NavLink and video picker view** + +## What Happened + +Added two protected lazy-loaded routes to App.tsx for the chapter review flow. Replaced the disabled Content placeholder in CreatorDashboard sidebar with an active Chapters NavLink. Added a VideoPicker component in ChapterReview.tsx that lists creator videos when no videoId is in the URL, using the consent API. Split ChapterReview into wrapper + ChapterReviewDetail for clean param handling. + +## Verification + +TypeScript compilation (npx tsc --noEmit) passes with no errors. All grep/test checks for ChapterReview in App.tsx, /creator/chapters in CreatorDashboard, file existence, and updateChapter in videos.ts pass. + +## Verification Evidence + +| # | Command | Exit Code | Verdict | Duration | +|---|---------|-----------|---------|----------| +| 1 | `cd frontend && npx tsc --noEmit` | 0 | ✅ pass | 5000ms | +| 2 | `grep -q 'ChapterReview' src/App.tsx` | 0 | ✅ pass | 50ms | +| 3 | `grep -q '/creator/chapters' src/pages/CreatorDashboard.tsx` | 0 | ✅ pass | 50ms | +| 4 | `test -f src/pages/ChapterReview.tsx` | 0 | ✅ pass | 10ms | +| 5 | `test -f src/pages/ChapterReview.module.css` | 0 | ✅ pass | 10ms | +| 6 | `grep -q 'updateChapter' src/api/videos.ts` | 0 | ✅ pass | 50ms | + + +## Deviations + +ChapterReview split into wrapper + ChapterReviewDetail sub-component. Video picker uses consent API rather than a dedicated endpoint. + +## Known Issues + +Video picker uses consent API which only shows videos with consent records. + +## Files Created/Modified + +- `frontend/src/App.tsx` +- `frontend/src/pages/CreatorDashboard.tsx` +- `frontend/src/pages/ChapterReview.tsx` +- `frontend/src/pages/ChapterReview.module.css` + + +## Deviations +ChapterReview split into wrapper + ChapterReviewDetail sub-component. Video picker uses consent API rather than a dedicated endpoint. + +## Known Issues +Video picker uses consent API which only shows videos with consent records. diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 248d533..09d5681 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -199,6 +199,7 @@ function AppShell() { }>} /> }>} /> }>} /> + }>} /> }>} /> {/* Fallback */} diff --git a/frontend/src/pages/ChapterReview.module.css b/frontend/src/pages/ChapterReview.module.css index c9529f5..63a7636 100644 --- a/frontend/src/pages/ChapterReview.module.css +++ b/frontend/src/pages/ChapterReview.module.css @@ -285,3 +285,51 @@ align-items: flex-start; } } + +/* ── Video Picker ─────────────────────────────────────────────────────────── */ + +.videoPickerList { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-top: 1rem; +} + +.videoPickerCard { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.875rem 1rem; + border-radius: 8px; + background: var(--surface-2, #1e293b); + border: 1px solid var(--border, #334155); + color: var(--text-primary, #e2e8f0); + text-decoration: none; + transition: background 0.15s, border-color 0.15s; +} + +.videoPickerCard:hover { + background: var(--surface-3, #2d3a4d); + border-color: var(--accent, #22d3ee); +} + +.videoPickerIcon { + width: 20px; + height: 20px; + flex-shrink: 0; + color: var(--text-secondary, #94a3b8); +} + +.videoPickerInfo { + display: flex; + flex-direction: column; + min-width: 0; +} + +.videoPickerName { + font-size: 0.9rem; + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/frontend/src/pages/ChapterReview.tsx b/frontend/src/pages/ChapterReview.tsx index f583dbe..b289f6f 100644 --- a/frontend/src/pages/ChapterReview.tsx +++ b/frontend/src/pages/ChapterReview.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { useParams } from "react-router-dom"; +import { Link, useParams } from "react-router-dom"; import WaveSurfer from "wavesurfer.js"; import RegionsPlugin from "wavesurfer.js/dist/plugins/regions.esm.js"; import { SidebarNav } from "./CreatorDashboard"; @@ -14,6 +14,7 @@ import { type ChapterPatch, type VideoDetail, } from "../api/videos"; +import { fetchConsentList, type VideoConsentRead } from "../api/consent"; import { useDocumentTitle } from "../hooks/useDocumentTitle"; import styles from "./ChapterReview.module.css"; @@ -49,12 +50,84 @@ function regionColor(status: string): string { } } +/* ── Video Picker (shown when no videoId in URL) ───────────────────────────── */ + +function VideoPicker() { + const [videos, setVideos] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + fetchConsentList() + .then((res) => { + if (!cancelled) setVideos(res.items); + }) + .catch((err) => { + if (!cancelled) + setError(err instanceof ApiError ? err.detail : "Failed to load videos"); + }) + .finally(() => { + if (!cancelled) setLoading(false); + }); + return () => { cancelled = true; }; + }, []); + + return ( +
+ +
+

Chapter Review

+

Select a video to review its chapters

+ + {loading &&
Loading videos…
} + {!loading && error &&
{error}
} + {!loading && !error && videos.length === 0 && ( +
+

No Videos

+

You don't have any videos yet.

+
+ )} + {!loading && !error && videos.length > 0 && ( +
+ {videos.map((v) => ( + + + + + +
+ {v.video_filename} +
+ + ))} +
+ )} +
+
+ ); +} + /* ── Main component ────────────────────────────────────────────────────────── */ export default function ChapterReview() { const { videoId } = useParams<{ videoId: string }>(); useDocumentTitle("Chapter Review"); + // If no videoId in URL, show the video picker + if (!videoId) return ; + + return ; +} + +/* ── Detail view (has a videoId) ───────────────────────────────────────────── */ + +function ChapterReviewDetail({ videoId }: { videoId: string }) { + const [chapters, setChapters] = useState([]); const [video, setVideo] = useState(null); const [loading, setLoading] = useState(true); diff --git a/frontend/src/pages/CreatorDashboard.tsx b/frontend/src/pages/CreatorDashboard.tsx index de1fefc..87398e8 100644 --- a/frontend/src/pages/CreatorDashboard.tsx +++ b/frontend/src/pages/CreatorDashboard.tsx @@ -24,17 +24,13 @@ function SidebarNav() { Dashboard - + - Content - + Chapters +