3.2.2 Board page + data layer — replace the `/boards` stub; fetch projection; board states
Estimate: 22m · Depends on: 3.2.1, 3.1.6
Stand up the real board route + its client data layer, the foundation the column/card/DnD subtasks build on. Replace the placeholder app/(authed)/boards/page.tsx (ProjectStubPage … comingIn="Epic 3") with the real surface; the sidebar + Cmd-K entries already point at /boards (Story 1.5), so NO navigation wiring changes.
Route + container. The page resolves the active project (the same active-project context the /issues route uses) and renders a client board container. The container fetches the projection from the Story-3.1.6 API (GET /api/projects/[key]/board → BoardProjectionDto: ordered columns, each with its first page of card DTOs + per-column total + cursor, plus top-level unmappedStatuses). Use the codebase's native client-fetch idiom (useState/useEffect/useTransition + fetch, as WorkflowEditor and the issue components do) — there is no query library and this story does not add one. Hold the projection in component state shaped so the later subtasks can mutate one column's card list in place (optimistic moves, appended pages) without refetching the whole board.
Quick-view wiring (card click → IssueQuickView modal). A board card is a peek into an issue: clicking a card opens the existing IssueQuickView modal (Story 2.5), the SAME peek surface the issue list uses — NOT a new detail surface and NOT a full-page navigation. This subtask mounts the shared quick-view panel at the board-page level (reusing the issues-list wiring — IssueQuickViewPanel + the QuickViewTrigger/QuickView open-handler/context) and threads an onOpenQuickView(workItemId) handler down to the BoardCard (3.2.3), which invokes it on click. The card stays keyboard-activatable (Enter opens the quick view; the drag pick-up key is Space, per 3.2.4) and a "↗ open full page" path from the peek still navigates to the issue detail. Reuse, do not rebuild, the 2.5 quick-view panel.
Board-level states (completeness). Render, per the 3.2.1 design: a loading skeleton (columns + card placeholders) via Spinner/skeleton while the projection streams; an error state (ErrorState with retry) on a failed fetch; and the no-board case (defensive — 3.1 auto-seeds one, but handle null). The project-has-no- issues empty state and the unmapped-statuses tray are the 3.2.6 subtask; this subtask renders the column scaffold + the load/error shells and leaves typed seams for them.
Out of scope here: the column/card components (3.2.3), drag-drop (3.2.4), lazy load-more/virtualization (3.2.5), and the empty/unmapped tray (3.2.6) — this subtask ships the route, the data fetch, the board state container, and the loading/error shells.
Acceptance criteria
/boardsrenders the real board (theProjectStubPageplaceholder is gone) for the active project; the sidebar/Cmd-K links resolve to it unchanged.- The container fetches
GET …/boardonce on load via the native fetch idiom (no query lib added) and holds the projection in mutable-per-column state;unmappedStatusesis carried in state for 3.2.6. - A loading skeleton shows while the projection streams; a failed fetch shows
ErrorStatewith a working retry; a null/absent board is handled without a crash. - The board page mounts the shared
IssueQuickViewpanel and provides theonOpenQuickView(workItemId)handler theBoardCard(3.2.3) calls; clicking a card opens the IssueQuickView modal (the same 2.5 peek surface — reused, not rebuilt), with the "open full page" path intact. - No data access in the page beyond calling the 3.1.6 API (no new route, no Prisma — this is a pure consumer); colours via
--el-*, shape via element tokens, matchingdesign/boards/. - Component/render tests assert the loading, error, and populated-scaffold branches, plus that a card click opens the quick view.
Context refs
app/(authed)/boards/page.tsx— the stub to replace;app/(authed)/_components/ProjectStubPage.tsx— what it renders todayapp/(authed)/issues/_components/IssueQuickView*.tsx+QuickViewTrigger.tsx— the quick-view panel + open-handler wiring to reuse (a card click opens it)app/(authed)/issues/page.tsx+_components/*— the active-project resolution + client-fetch + Suspense/skeleton precedent to mirror- Story 3.1.6 — the
GET /api/projects/[key]/boardroute +BoardProjectionDto/lib/dto/boards.tsshape this consumes components/ui/Spinner/ErrorState/EmptyState;design/boards/board.mock.html+design-notes.md(3.2.1)motir-core/CLAUDE.md—--el-*colour rule, element-shape rule (this is client UI, no service/repo work)