Files
GearBox/.planning/phases/11-candidate-ranking/11-02-SUMMARY.md
Jean-Luc Makiola 4304d0fcd7 docs(11-02): complete drag-to-reorder ranking UI plan
- Add 11-02-SUMMARY.md with implementation details and deviation docs
- Update STATE.md: progress 100%, decisions, session record
- Update ROADMAP.md: phase 11 complete (2/2 plans with summaries)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 22:30:32 +01:00

86 lines
4.5 KiB
Markdown

---
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 `<RankBadge rank={rank} />` in badge row
- Updated `threads/$threadId.tsx`:
- List/grid view toggle (LayoutList / LayoutGrid icons) using `candidateViewMode` from uiStore
- Active list view: `<Reorder.Group>` wrapping `<CandidateListItem>` instances for drag-to-reorder
- Resolved list view: plain `<div>` with `<CandidateListItem isActive={false}>` — rank badges visible, drag handles hidden
- Grid view: existing `<CandidateCard>` 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.