import { Reorder } from "framer-motion"; import { useRef } from "react"; import { useFormatters } from "../hooks/useFormatters"; import type { CandidateDelta } from "../hooks/useImpactDeltas"; import { LucideIcon } from "../lib/iconData"; import { useUIStore } from "../stores/uiStore"; import { ImpactDeltaBadge } from "./ImpactDeltaBadge"; import { StatusBadge } from "./StatusBadge"; interface CandidateWithCategory { id: number; threadId: number; name: string; weightGrams: number | null; priceCents: number | null; categoryId: number; notes: string | null; productUrl: string | null; imageFilename: string | null; status: "researching" | "ordered" | "arrived"; pros: string | null; cons: string | null; createdAt: string; updatedAt: string; categoryName: string; categoryIcon: string; } interface CandidateListItemProps { candidate: CandidateWithCategory; rank: number; isActive: boolean; onStatusChange: (status: "researching" | "ordered" | "arrived") => void; delta?: CandidateDelta; onDragEnd?: () => void; } const RANK_COLORS = ["#D4AF37", "#C0C0C0", "#CD7F32"]; // gold, silver, bronze export function RankBadge({ rank }: { rank: number }) { if (rank > 3) return null; return ( ); } export function CandidateListItem({ candidate, rank, isActive, onStatusChange, delta, onDragEnd, }: CandidateListItemProps) { const isDragging = useRef(false); const { weight, price } = useFormatters(); const openCandidateEditPanel = useUIStore((s) => s.openCandidateEditPanel); const openConfirmDeleteCandidate = useUIStore( (s) => s.openConfirmDeleteCandidate, ); const openResolveDialog = useUIStore((s) => s.openResolveDialog); const openExternalLink = useUIStore((s) => s.openExternalLink); const sharedClassName = "flex items-center gap-3 bg-white rounded-xl border border-gray-100 p-3 hover:border-gray-200 hover:shadow-sm group cursor-default"; const innerContent = ( <> {/* Drag handle indicator */} {isActive && ( )} {/* Rank badge */} {/* Image thumbnail */}
{candidate.imageFilename ? ( {candidate.name} ) : ( )}
{/* Name + badges */} {/* Action buttons (hover-reveal) */}
{isActive && ( )} {candidate.productUrl && ( )}
); // Reorder.Item requires a Reorder.Group parent — only use it in active threads if (isActive) { return ( { isDragging.current = true; }} onDragEnd={() => { setTimeout(() => { isDragging.current = false; }, 0); onDragEnd?.(); }} whileDrag={{ cursor: "grabbing" }} style={{ marginBottom: 8, cursor: "grab" }} className={sharedClassName} > {innerContent} ); } return
{innerContent}
; }