Transform the current full-page project views into a master-detail layout:
- Left panel (master): a scrollable, narrow list of all projects — always visible
- Right panel (detail): the selected project's details including its task list
- Clicking a project in the master list highlights it and updates the right panel
- The URL updates to
/projects/:projectIdso the selection is bookmarkable - When no project is selected: "Select a project to view details" placeholder
- Task detail view at
/projects/:projectId/tasks/:taskIdwith a "Back to tasks" link
- Nested routes with
<Outlet />as a content slot at multiple levels - URL as the source of truth for UI state (selected project, selected task)
useParamsat different route depths to readprojectIdandtaskIdNavLink(or manualuseParamscomparison) for highlighting the selected item- Scroll preservation: the master list does not scroll to the top on selection change
The start/ directory is derived from Challenge 10's solution. It has:
- React Router v7 configured with
BrowserRouter - A grid layout shell (Header, Sidebar, Outlet, Footer) in
Layout.tsx ProjectListPage— full-page project grid with filteringProjectDetailPage— full-page project detail (readsuseParams)NewProjectPage— add project form as a full page- Routes:
/projects,/projects/new,/projects/:projectId
The list and detail are separate full-page views — no side-by-side layout yet.
Create src/pages/ProjectsLayout.tsx:
- Two-column layout: narrow left panel (master list) + wide right panel (detail)
- Left panel: renders a list of all projects, always visible, scrollable
- Right panel: renders
<Outlet />— child routes fill this slot - Use CSS Grid or flexbox
Create src/components/ProjectListItem.tsx:
- Compact single-row item: project name, status badge, task count
- Highlighted (visually distinct) when its project is currently selected
- Check the current
projectIdfromuseParams()to determine if selected - Clicking navigates to
/projects/:projectId
Restructure routes to nest under ProjectsLayout:
<Route element={<Layout />}>
<Route path="/projects" element={<ProjectsLayout />}>
<Route index element={<ProjectEmptyState />} />
<Route path=":projectId" element={<ProjectDetailPanel />}>
<Route index element={<TaskList />} />
<Route path="tasks/:taskId" element={<TaskDetail />} />
</Route>
</Route>
<Route path="/projects/new" element={<NewProjectPage />} />
</Route>Create src/pages/ProjectDetailPanel.tsx:
- Reads
projectIdfromuseParams - Shows project name, description, status, due date
- Renders
<Outlet />below the project info for the task list or task detail
Create src/components/TaskList.tsx:
- Reads
projectIdfromuseParams - Filters tasks from
src/data/tasks.tsfor the current project - Renders a
TaskItemfor each task - Each task navigates to
/projects/:projectId/tasks/:taskIdon click
Create src/components/TaskItem.tsx:
- Compact task row: title, status badge, assignee name (if any)
- Wrapped in a
Linkto the task detail route
Create src/pages/TaskDetail.tsx:
- Reads
taskId(andprojectId) fromuseParams - Shows full task details: title, description, status, assignee, due date
- "Back to tasks" link: navigates to
/projects/:projectId
Create src/pages/ProjectEmptyState.tsx:
- Simple placeholder: "Select a project to view details"
- Rendered when no project is selected (index route under
/projects)
Add hardcoded tasks data (subset of shared/mock-api/data.ts) associated with proj-1 and proj-2.
Add TaskStatus and Task types.
.projects-layout {
display: grid;
grid-template-columns: 320px 1fr;
gap: 1.5rem;
height: calc(100vh - var(--header-height) - 3rem);
}
.master-list {
overflow-y: auto;
border-right: 1px solid var(--color-border);
padding-right: 1rem;
}
.detail-panel {
overflow-y: auto;
}- Navigating to
/projectsshows the two-column layout - Clicking a project item highlights it in the master list
- The right panel shows the selected project's details and tasks
- Navigating to
/projects(no selection) shows the empty state placeholder - The master list does NOT unmount or scroll-reset when a project is selected
-
/projects/:projectId/tasks/:taskIdshows task detail with a working "Back to tasks" link - The URL reflects the selected project and task at all times
- TypeScript strict mode — no
any
Why nested routes instead of state?
Storing the selected project in URL params (/projects/proj-1) rather than in useState means:
- The URL is shareable and bookmarkable
- The browser Back button works naturally
- Deep-linking directly to a task works out of the box
<Outlet /> at multiple levels
Layouthas an<Outlet />→ rendersProjectsLayoutorNewProjectPageProjectsLayouthas an<Outlet />→ rendersProjectEmptyStateorProjectDetailPanelProjectDetailPanelhas an<Outlet />→ rendersTaskListorTaskDetail
Scroll preservation
The master list is a separate scrollable container from the detail panel. Selecting a different project only re-renders the detail <Outlet />, not the entire master list, so the list's scroll position is preserved.