Deal detail page redesign — Implementation Plan
15 TDD tasks. One commit per task. New full-page deal detail view at /admin/queue/$dealId replaces the slide-out modal. Real Postgres + MSW + respx for tests.
TL;DRSummary
Replaces the RunDetailSheet slide-out with a full-page detail view at /admin/queue/$dealId. Adds one VCC client method (get_appraisal_contents), one backend route (GET /admin/queue/deals/{deal_uuid}/appraisal), nine nullable per-item appraisal fields on DealItemBrief, seven new frontend components under web/src/components/deal-detail/, and a new file route.
Architecture: No DB migrations. New appraisal fields stay null until sub-project B builds the appraisal stage. The *IO components grow a temporary hideItems prop so they can be reused in the new StageDetailPanel without their items grid; the prop and items-rendering code are removed entirely in the final cleanup task. Auto-poll 3 s while any stage is running.
§0Working agreements
- TDD. Failing test first, minimal impl, run green, commit.
- One commit per task (15 focused commits).
- Each task ships working software — no broken suite, no broken dev server, between tasks.
- Leave the user's WIP alone (segmenter.py, test_segmenter_test_route.py, admin.testing.segmentation* routes, SegmenterTab.tsx, DealImagePickerModal*).
- No DB migrations. Pydantic-only changes for the new appraisal fields.
§1File map
Created
| Path | Responsibility |
|---|---|
| backend/tests/test_vcc_client_appraisal_contents.py | respx-mocked unit tests for the new VCC client method |
| backend/tests/test_appraisal_route.py | Route tests for the new appraisal endpoint |
| web/src/components/deal-detail/DealHeader.tsx (+ test) | Identity / pipeline / VCC state card |
| web/src/components/deal-detail/DealTimeline.tsx (+ test) | Horizontal pipeline stage strip; unifies PipelineTrail + TimelineTab |
| web/src/components/deal-detail/ItemCard.tsx (+ test) | Read-only per-item card |
| web/src/components/deal-detail/ItemsGrid.tsx (+ test) | Page-level always-visible items grid |
| web/src/components/deal-detail/StageDetailPanel.tsx (+ test) | Wraps trimmed *IO components + inline error block |
| web/src/components/deal-detail/VccAppraisalCard.tsx (+ test) | VCC human-appraisal display |
| web/src/components/deal-detail/LogsDrawer.tsx (+ test) | Slide-up unified-log drawer with stage-boundary markers |
| web/src/routes/admin.queue.$dealId.tsx (+ test) | New file route assembling the page |
Modified
| Path | Change |
|---|---|
| backend/.../vcc/client.py | Add VccClient.get_appraisal_contents |
| backend/.../api/routers/admin.py | Add GET /admin/queue/deals/{deal_uuid}/appraisal + AppraisalContentsResponse |
| backend/.../features/deals/schemas.py | 9 nullable fields on DealItemBrief |
| backend/.../features/deals/service.py | Comment in get_deal_detail noting null defaults |
| backend/tests/test_deal_detail_schemas.py + test_deal_queue_service_detail.py | Cover new fields |
| openapi.yaml + web/src/lib/api/generated/* | Regenerated (T4) |
| web/src/lib/api/adapters/deal-detail.ts (+ test) | ItemInfo grows 9 new fields |
| web/src/components/queue/tabs/io/*IO.tsx | Add hideItems prop (T10), remove items-rendering entirely (T15) |
| web/src/routes/admin.production.queue.tsx | Row click navigates; backwards-compat redirect for ?run= |
Deleted (T15)
| Path | Reason |
|---|---|
| web/src/components/queue/RunDetailSheet.tsx (+ test) | Slide-out replaced by full page |
| web/src/components/queue/PipelineTrail.tsx | Folded into DealTimeline |
| web/src/components/queue/tabs/TimelineTab.tsx | Redundant with timeline |
| web/src/components/queue/tabs/IOTab.tsx (+ test) | Replaced by StageDetailPanel |
| web/src/components/queue/tabs/LogsTab.tsx (+ test) | Replaced by LogsDrawer |
| web/src/components/queue/tabs/ErrorTab.tsx (+ test) | Inlined into StageDetailPanel |
§2Phase 0 · Backend foundation
VccClient.get_appraisal_contentstddmodifyintegrations/vcc/client.py · tests/test_vcc_client_appraisal_contents.pyapi/routers/admin.py · features/deals/schemas.py · tests/test_appraisal_route.pyGET /admin/queue/deals/{deal_uuid}/appraisal: 404 on unknown deal; 200 + payload from VccClient; 200 + null when VCC returns None; 502 on VccError. Response model has model_config = ConfigDict(extra="allow") so we accept whatever VCC returns.DealItemBrief with 9 nullable appraisal fieldstddmodifyfeatures/deals/schemas.py · features/deals/service.py · tests/test_deal_detail_schemas.py · tests/test_deal_queue_service_detail.pyopenapi.yaml · web/src/lib/api/generated/*make openapi then bun run codegen. Stage only the relevant openapi.yaml hunks — the user's TestSegmentationRequest hunk is unrelated WIP. Use git add -p to pick the appraisal-route and DealItemBrief hunks only.§3Phase 1 · Adapter
ItemInfo in deal-detail.ts adaptertddmodifyweb/src/lib/api/adapters/deal-detail.ts (+ test)ItemInfo (camelCased). Adapter copies them through. Two new tests cover null-default and populated round-trip.§4Phase 2 · Components
ItemCard — read-only per-item summarytddcreateweb/src/components/deal-detail/ItemCard.tsx (+ test)ItemsGrid — responsive grid of cardstddcreateweb/src/components/deal-detail/ItemsGrid.tsx (+ test)DealHeader — identity + pipeline + VCC statetddcreateweb/src/components/deal-detail/DealHeader.tsx (+ test)DealTimeline — horizontal clickable stage striptddcreateweb/src/components/deal-detail/DealTimeline.tsx (+ test)StageDetailPanel + hideItems prop on *IOtddcreatequeue/tabs/io/*IO.tsx · deal-detail/StageDetailPanel.tsx (+ test)VccAppraisalCard — payload | placeholder | errortddcreateweb/src/components/deal-detail/VccAppraisalCard.tsx (+ test)LogsDrawer — slide-up panel with stage markerstddcreateweb/src/components/deal-detail/LogsDrawer.tsx (+ test)§5Phase 3 · Page wiring
/admin/queue/$dealId + auto-polltddcreateweb/src/routes/admin.queue.$dealId.tsx (+ test)web/src/routes/admin.production.queue.tsx · web/src/components/queue/QueueTable.tsxnavigate({ to: "/admin/queue/$dealId", params: { dealId } }). Add one-line backwards-compat shim at the top of the queue route: if search.run is present, <Navigate to="/admin/queue/$dealId" ... replace />. Remove the <RunDetailSheet/> render from the queue route.§6Phase 4 · Cleanup
hideItems propdeleteRunDetailSheet, PipelineTrail, TimelineTab, IOTab, LogsTab, ErrorTab + tests · *IO.tsx trimDoDDefinition of Done
cd backend && uv run pytest -q— all green.cd backend && make backend-lint— clean (ruff + mypy strict).cd web && bun run vitest run— all green.cd web && bun run lint— no new errors.git log --onelineshows 15 focused commits for this plan.- Smoke test: process a real deal end-to-end via docker compose; new detail page renders timeline / items / stage panel / VCC appraisal / logs drawer.
- No references to deleted components remain in
web/src/.
openapi-ts) depend on the OpenAPI operationIds. After T4's codegen, run grep "DealDetail\|DealAppraisal" web/src/lib/api/generated/@tanstack/react-query.gen.ts and adjust T13's imports if the names differ from the plan.