--- phase: 11-candidate-ranking plan: "02" subsystem: client-ui tags: [drag-reorder, framer-motion, rank-badges, view-toggle, list-view] dependency_graph: requires: [11-01] provides: [drag-to-reorder-ui, rank-badges, list-grid-toggle] affects: [threads/$threadId.tsx, CandidateCard, CandidateListItem, uiStore, useCandidates] tech_stack: added: [] patterns: - framer-motion Reorder.Group + Reorder.Item with useDragControls for drag handle - tempItems pattern to prevent React Query flicker during optimistic drag - RankBadge exported from CandidateListItem for reuse across views - candidateViewMode in uiStore for list/grid toggle state key_files: created: - src/client/components/CandidateListItem.tsx modified: - src/client/hooks/useCandidates.ts - src/client/stores/uiStore.ts - src/client/components/CandidateCard.tsx - src/client/routes/threads/$threadId.tsx - src/client/lib/iconData.tsx - src/client/hooks/useThreads.ts decisions: - Resolved thread list view uses plain div (not Reorder.Group) — no drag, rank badges visible - handleDragEnd fires on Reorder.Group onPointerUp to debounce reorder API call - biome-ignore applied to useExhaustiveDependencies for thread?.candidates dep — intentional trigger metrics: duration: 4min completed: 2026-03-16T21:29:07Z tasks_completed: 3 files_changed: 7 --- # Phase 11 Plan 02: Drag-to-Reorder UI Summary Drag-to-reorder candidate ranking with list/grid view toggle, gold/silver/bronze rank badges, and framer-motion Reorder.Group with tempItems flicker prevention. ## What Was Built ### Task 1: useReorderCandidates hook + uiStore view mode + CandidateListItem component - Added `useReorderCandidates` mutation hook to `useCandidates.ts` using `apiPatch` to hit `PATCH /api/threads/:id/candidates/reorder` - Added `candidateViewMode: "list" | "grid"` and `setCandidateViewMode` to `uiStore.ts` - Created `CandidateListItem.tsx` — horizontal card for list view with drag handle (GripVertical), RankBadge (medal icon), 48x48 image thumbnail, badge row (weight/price/category/status/pros-cons), and hover-reveal action buttons - Exported `RankBadge` component (gold `#D4AF37`, silver `#C0C0C0`, bronze `#CD7F32`) for reuse - Added `style` prop support to `LucideIcon` for colored medal icons - Added `pros` and `cons` fields to `CandidateWithCategory` in `useThreads.ts` (Rule 2 auto-fix — missing after Phase 10) ### Task 2: Thread detail page with view toggle, Reorder.Group, rank badges in grid view - Updated `CandidateCard.tsx`: added `rank?: number` prop and renders `` in badge row - Updated `threads/$threadId.tsx`: - List/grid view toggle (LayoutList / LayoutGrid icons) using `candidateViewMode` from uiStore - Active list view: `` wrapping `` instances for drag-to-reorder - Resolved list view: plain `
` with `` — rank badges visible, drag handles hidden - Grid view: existing `` grid with `rank={index + 1}` passed to each card - `tempItems` state: holds in-progress drag order, falls back to `thread.candidates` when null - `handleDragEnd`: fires `reorderMutation.mutate({ orderedIds })` and clears tempItems on settled - `useEffect` clears tempItems when `thread?.candidates` reference changes (fresh server data) ### Task 3: Human verification (auto-approved — auto_chain_active) ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 2 - Missing] Added pros/cons fields to CandidateWithCategory in useThreads.ts** - **Found during:** Task 1 (needed by CandidateListItem) - **Issue:** `CandidateWithCategory` interface in `useThreads.ts` was missing `pros` and `cons` fields added in Phase 10. CandidateListItem needed these to render the "+/- Notes" badge. - **Fix:** Added `pros: string | null` and `cons: string | null` to the interface - **Files modified:** `src/client/hooks/useThreads.ts` - **Commit:** acfa995 **2. [Rule 2 - Missing] Added style prop to LucideIcon component** - **Found during:** Task 1 (needed by RankBadge for medal colors) - **Issue:** LucideIcon only accepted `className` for styling; RankBadge needed inline `style={{ color }}` for gold/silver/bronze hex colors not achievable via Tailwind - **Fix:** Added optional `style?: React.CSSProperties` prop to LucideIcon and passed through to icon component - **Files modified:** `src/client/lib/iconData.tsx` - **Commit:** acfa995 ## Self-Check: PASSED All created/modified files exist. Both task commits (acfa995, 94c07e7) confirmed in git log.