--- phase: 13-setup-impact-preview plan: 02 type: execute wave: 2 depends_on: ["13-01"] files_modified: - src/client/components/SetupImpactSelector.tsx - src/client/components/ImpactDeltaBadge.tsx - src/client/routes/threads/$threadId.tsx - src/client/components/CandidateListItem.tsx - src/client/components/CandidateCard.tsx - src/client/components/ComparisonTable.tsx autonomous: true requirements: [IMPC-01, IMPC-02, IMPC-03, IMPC-04] must_haves: truths: - "User can select a setup from a dropdown in the thread header" - "Each candidate displays weight and cost delta badges when a setup is selected" - "Replace mode shows signed delta with replaced item name context" - "Add mode shows positive delta labeled as '(add)'" - "Candidate with no weight shows '-- (no weight data)' instead of a zero" - "Candidate with no price shows '-- (no price data)' instead of a zero" - "Deselecting setup ('None') clears all delta indicators" - "Deltas appear in list view, grid view, and comparison table" artifacts: - path: "src/client/components/SetupImpactSelector.tsx" provides: "Setup dropdown for thread header" exports: ["SetupImpactSelector"] - path: "src/client/components/ImpactDeltaBadge.tsx" provides: "Inline delta indicator component" exports: ["ImpactDeltaBadge"] - path: "src/client/routes/threads/$threadId.tsx" provides: "Thread detail page wired with impact preview" - path: "src/client/components/CandidateListItem.tsx" provides: "List item with delta badges" - path: "src/client/components/CandidateCard.tsx" provides: "Card with delta badges" - path: "src/client/components/ComparisonTable.tsx" provides: "Comparison table with impact delta rows" key_links: - from: "src/client/routes/threads/$threadId.tsx" to: "src/client/hooks/useImpactDeltas.ts" via: "useImpactDeltas hook call at page level" pattern: "useImpactDeltas" - from: "src/client/routes/threads/$threadId.tsx" to: "src/client/hooks/useSetups.ts" via: "useSetup(selectedSetupId) for setup item data" pattern: "useSetup\\(selectedSetupId" - from: "src/client/routes/threads/$threadId.tsx" to: "src/client/stores/uiStore.ts" via: "selectedSetupId state read" pattern: "useUIStore.*selectedSetupId" - from: "src/client/components/SetupImpactSelector.tsx" to: "src/client/stores/uiStore.ts" via: "setSelectedSetupId state write" pattern: "setSelectedSetupId" - from: "src/client/components/ImpactDeltaBadge.tsx" to: "src/client/lib/impactDeltas.ts" via: "CandidateDelta type import" pattern: "import.*CandidateDelta" - from: "src/client/components/ComparisonTable.tsx" to: "src/client/lib/impactDeltas.ts" via: "ImpactDeltas type for deltas prop" pattern: "import.*ImpactDeltas" --- Build the UI components (setup dropdown + delta badges) and wire them into the thread detail page across all three view modes (list, grid, compare). Purpose: This is the user-facing delivery of the impact preview feature. Plan 01 built the logic; this plan renders it. Output: SetupImpactSelector component, ImpactDeltaBadge component, updated CandidateListItem/CandidateCard/ComparisonTable with delta rendering, wired thread detail page. @/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md @/home/jlmak/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/13-setup-impact-preview/13-RESEARCH.md @.planning/phases/13-setup-impact-preview/13-01-SUMMARY.md From src/client/lib/impactDeltas.ts (created in Plan 01): ```typescript export interface CandidateInput { id: number; weightGrams: number | null; priceCents: number | null; } export type DeltaMode = "replace" | "add" | "none"; export interface CandidateDelta { candidateId: number; mode: DeltaMode; weightDelta: number | null; priceDelta: number | null; replacedItemName: string | null; } export interface ImpactDeltas { mode: DeltaMode; deltas: Record; } ``` From src/client/hooks/useImpactDeltas.ts (created in Plan 01): ```typescript export function useImpactDeltas( candidates: CandidateInput[], setupItems: SetupItemWithCategory[] | undefined, threadCategoryId: number, ): ImpactDeltas; ``` From src/client/hooks/useSetups.ts: ```typescript export function useSetups(): UseQueryResult; export function useSetup(setupId: number | null): UseQueryResult; ``` From src/client/stores/uiStore.ts (updated in Plan 01): ```typescript selectedSetupId: number | null; setSelectedSetupId: (id: number | null) => void; ``` From src/client/hooks/useThreads.ts (updated in Plan 01): ```typescript interface ThreadWithCandidates { id: number; name: string; status: "active" | "resolved"; resolvedCandidateId: number | null; categoryId: number; // <-- Added in Plan 01 createdAt: string; updatedAt: string; candidates: CandidateWithCategory[]; } ``` From src/client/lib/formatters.ts: ```typescript export function formatWeight(grams: number | null | undefined, unit: WeightUnit = "g"): string; export function formatPrice(cents: number | null | undefined, currency: Currency = "USD"): string; ``` Existing component props that need delta additions: CandidateListItem props: ```typescript interface CandidateListItemProps { candidate: CandidateWithCategory; rank: number; isActive: boolean; onStatusChange: (status: "researching" | "ordered" | "arrived") => void; // Will add: delta?: CandidateDelta; } ``` CandidateCard props: ```typescript interface CandidateCardProps { id: number; name: string; weightGrams: number | null; priceCents: number | null; // ... other props // Will add: delta?: CandidateDelta; } ``` ComparisonTable props: ```typescript interface ComparisonTableProps { candidates: CandidateWithCategory[]; resolvedCandidateId: number | null; // Will add: deltas?: Record; } ``` Task 1: Create SetupImpactSelector and ImpactDeltaBadge components src/client/components/SetupImpactSelector.tsx, src/client/components/ImpactDeltaBadge.tsx 1. Create src/client/components/SetupImpactSelector.tsx: - Import useSetups from hooks/useSetups - Import useUIStore from stores/uiStore - Export function SetupImpactSelector() - Read selectedSetupId and setSelectedSetupId from uiStore - Fetch setups via useSetups() - If no setups or loading, return null - Render: a flex row with label "Impact on setup:" (text-xs text-gray-500) and a native `