Problem
frontend/src/app/projects/[slug]/page.tsx is 835 lines — a god component holding state for project data, active section, token stats, settings, chat messages, streaming state, selection context, preview dialog, comments, and multiple loading states all in one useState pile.
Step 1: Extract custom hooks
frontend/src/hooks/use-project-data.ts
Encapsulates: project loading, section loading, comments loading, settings.
Returns: { project, activeSection, settings, allComments, loading, sectionLoading, loadProject, selectSection, refreshComments }
frontend/src/hooks/use-chat.ts
Encapsulates: chat messages, streaming state, abort controller, selection context, pending approval, stream status/tools.
Returns: { messages, isStreaming, streamStatus, pendingApproval, selectionContext, sendMessage, stopStreaming, approveToolUse, setSelectionContext, clearChat }
Step 2: Extract tab content components
frontend/src/components/changelog-tab.tsx — receives changelog array, renders timeline
frontend/src/components/comments-tab.tsx — receives allComments, renders grouped by section
frontend/src/components/prd-preview-dialog.tsx — receives project + sections, renders full PRD in Dialog with markdown + PDF export button
Step 3: Simplify page component
After extraction, projects/[slug]/page.tsx should be ~300 lines: layout shell, tab switching, and wiring hooks to child components. No business logic in the page itself.
Acceptance Criteria
Problem
frontend/src/app/projects/[slug]/page.tsxis 835 lines — a god component holding state for project data, active section, token stats, settings, chat messages, streaming state, selection context, preview dialog, comments, and multiple loading states all in oneuseStatepile.Step 1: Extract custom hooks
frontend/src/hooks/use-project-data.tsEncapsulates: project loading, section loading, comments loading, settings.
Returns:
{ project, activeSection, settings, allComments, loading, sectionLoading, loadProject, selectSection, refreshComments }frontend/src/hooks/use-chat.tsEncapsulates: chat messages, streaming state, abort controller, selection context, pending approval, stream status/tools.
Returns:
{ messages, isStreaming, streamStatus, pendingApproval, selectionContext, sendMessage, stopStreaming, approveToolUse, setSelectionContext, clearChat }Step 2: Extract tab content components
frontend/src/components/changelog-tab.tsx— receiveschangelogarray, renders timelinefrontend/src/components/comments-tab.tsx— receivesallComments, renders grouped by sectionfrontend/src/components/prd-preview-dialog.tsx— receivesproject+sections, renders full PRD in Dialog with markdown + PDF export buttonStep 3: Simplify page component
After extraction,
projects/[slug]/page.tsxshould be ~300 lines: layout shell, tab switching, and wiring hooks to child components. No business logic in the page itself.Acceptance Criteria
yarn typecheckpassesyarn lintpasses