---
phase: 04-database-planning-fixes
plan: 02
type: execute
wave: 2
depends_on: [04-01]
files_modified:
  - src/client/stores/uiStore.ts
  - src/client/components/CreateThreadModal.tsx
  - src/client/components/ThreadCard.tsx
  - src/client/routes/collection/index.tsx
autonomous: false
requirements: [PLAN-01, PLAN-02]

must_haves:
  truths:
    - "User can create a thread via a modal dialog with name and category fields"
    - "User sees an inviting empty state explaining the 3-step planning workflow when no threads exist"
    - "User can switch between Active and Resolved threads using pill tabs"
    - "Thread cards display category icon and name"
  artifacts:
    - path: "src/client/components/CreateThreadModal.tsx"
      provides: "Modal dialog for thread creation with name + category picker"
      min_lines: 60
    - path: "src/client/routes/collection/index.tsx"
      provides: "PlanningView with empty state, pill tabs, category filter, modal trigger"
      contains: "CreateThreadModal"
    - path: "src/client/components/ThreadCard.tsx"
      provides: "Thread card with category display"
      contains: "categoryEmoji"
  key_links:
    - from: "src/client/components/CreateThreadModal.tsx"
      to: "src/client/hooks/useThreads.ts"
      via: "useCreateThread mutation with { name, categoryId }"
      pattern: "useCreateThread"
    - from: "src/client/routes/collection/index.tsx"
      to: "src/client/components/CreateThreadModal.tsx"
      via: "createThreadModalOpen state from uiStore"
      pattern: "CreateThreadModal"
    - from: "src/client/components/ThreadCard.tsx"
      to: "ThreadListItem"
      via: "categoryName and categoryEmoji props"
      pattern: "categoryEmoji|categoryName"
---

<objective>
Build the frontend for thread creation modal, polished empty state, Active/Resolved pill tabs, category filter, and category display on thread cards.

Purpose: PLAN-01 (user can create threads without errors via modal) and PLAN-02 (polished empty state with CTA). This completes the planning tab UX overhaul.
Output: Working planning tab with modal-based thread creation, educational empty state, pill tab filtering, and category-aware thread cards.
</objective>

<execution_context>
@/home/jean-luc-makiola/.claude/get-shit-done/workflows/execute-plan.md
@/home/jean-luc-makiola/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/ROADMAP.md
@.planning/phases/04-database-planning-fixes/04-CONTEXT.md
@.planning/phases/04-database-planning-fixes/04-01-SUMMARY.md

<interfaces>
<!-- From Plan 01: updated types the executor will consume -->

From src/client/hooks/useThreads.ts (after Plan 01):
```typescript
interface ThreadListItem {
  id: number;
  name: string;
  status: "active" | "resolved";
  resolvedCandidateId: number | null;
  createdAt: string;
  updatedAt: string;
  candidateCount: number;
  minPriceCents: number | null;
  maxPriceCents: number | null;
  categoryId: number;
  categoryName: string;
  categoryEmoji: string;
}

// useCreateThread expects { name: string; categoryId: number }
```

From src/client/hooks/useCategories.ts:
```typescript
export function useCategories(): UseQueryResult<Category[]>;
// Category = { id: number; name: string; emoji: string; createdAt: Date }
```

From src/client/stores/uiStore.ts (needs createThreadModal state added):
```typescript
// Existing pattern for dialogs:
// resolveThreadId: number | null;
// openResolveDialog: (threadId, candidateId) => void;
// closeResolveDialog: () => void;
```

From src/client/routes/collection/index.tsx (CollectionView empty state pattern):
```typescript
// Lines 58-93: empty state with emoji, heading, description, CTA button
// Follow this pattern for planning empty state
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create thread modal and update uiStore</name>
  <files>src/client/stores/uiStore.ts, src/client/components/CreateThreadModal.tsx</files>
  <action>
1. In `src/client/stores/uiStore.ts`, add create-thread modal state following the existing dialog pattern:
   ```
   createThreadModalOpen: boolean;
   openCreateThreadModal: () => void;
   closeCreateThreadModal: () => void;
   ```
   Initialize `createThreadModalOpen: false` and wire up the actions.

2. Create `src/client/components/CreateThreadModal.tsx`:
   - A modal overlay (fixed inset-0, bg-black/50 backdrop, centered white panel) following the same pattern as the app's existing dialog styling.
   - Form fields: Thread name (text input, required, min 1 char) and Category (select dropdown populated from `useCategories()` hook).
   - Category select shows emoji + name for each option. Pre-select the first category.
   - Submit calls `useCreateThread().mutate({ name, categoryId })`.
   - On success: close modal (via `closeCreateThreadModal` from uiStore), reset form.
   - On error: show inline error message.
   - Cancel button and clicking backdrop closes modal.
   - Disable submit button while `isPending`.
   - Use Tailwind classes consistent with existing app styling (rounded-xl, text-sm, blue-600 primary buttons, gray-200 borders).
  </action>
  <verify>
    <automated>cd /home/jean-luc-makiola/Development/projects/GearBox && bun run lint 2>&1 | tail -5</automated>
  </verify>
  <done>CreateThreadModal component renders a modal with name input and category dropdown, submits via useCreateThread, uiStore has createThreadModalOpen state</done>
</task>

<task type="auto">
  <name>Task 2: Overhaul PlanningView with empty state, pill tabs, category filter, and thread card category display</name>
  <files>src/client/routes/collection/index.tsx, src/client/components/ThreadCard.tsx</files>
  <action>
1. In `src/client/components/ThreadCard.tsx`:
   - Add `categoryName: string` and `categoryEmoji: string` props to `ThreadCardProps`.
   - Display category as a pill badge (emoji + name) in the card's badge row, using a style like `bg-blue-50 text-blue-700` to distinguish from existing badges.

2. In `src/client/routes/collection/index.tsx`, rewrite the `PlanningView` function:

   **Remove:** The inline text input + button form for thread creation. Remove the `showResolved` checkbox.

   **Add state:**
   - `activeTab: "active" | "resolved"` (default "active") for the pill tab selector.
   - `categoryFilter: number | null` (default null = all categories) for filtering.
   - Import `useCategories` hook, `useUIStore`, and `CreateThreadModal`.

   **Layout (top to bottom):**

   a. **Header row:** "Planning Threads" heading on the left, "New Thread" button on the right. Button calls `openCreateThreadModal()` from uiStore. Use a plus icon (inline SVG, same pattern as collection empty state button).

   b. **Filter row:** Active/Resolved pill tab selector on the left, category filter dropdown on the right.
      - Pill tabs: Two buttons styled as a segment control. Active pill gets `bg-blue-600 text-white`, inactive gets `bg-gray-100 text-gray-600 hover:bg-gray-200`. Rounded-full, px-4 py-1.5, text-sm font-medium. Wrap in a `flex bg-gray-100 rounded-full p-0.5 gap-0.5` container.
      - Category filter: A `<select>` dropdown with "All categories" as default option, then each category with emoji + name. Filter threads client-side by matching `thread.categoryId === categoryFilter`.

   c. **Thread list or empty state:**
      - Pass `activeTab === "resolved"` as `includeResolved` to `useThreads`. When `activeTab === "active"`, show only active threads. When `activeTab === "resolved"`, filter the results to show only resolved threads (since `includeResolved=true` returns both).
      - Apply `categoryFilter` on the client side if set.

   d. **Empty state (when filtered threads array is empty AND activeTab is "active" AND no category filter):**
      - Guided + educational tone per user decision.
      - Max-width container (max-w-lg mx-auto), centered, py-16.
      - Heading: "Plan your next purchase" (text-xl font-semibold).
      - Three illustrated steps showing the workflow, each as a row with a step number circle (1, 2, 3), a short title, and a description:
        1. "Create a thread" -- "Start a research thread for gear you're considering"
        2. "Add candidates" -- "Add products you're comparing with prices and weights"
        3. "Pick a winner" -- "Resolve the thread and the winner joins your collection"
      - Style each step: flex row, step number in a 8x8 rounded-full bg-blue-100 text-blue-700 font-bold circle, title in font-medium, description in text-sm text-gray-500.
      - CTA button below steps: "Create your first thread" -- calls `openCreateThreadModal()`. Blue-600 bg, white text, same style as collection empty state button.
      - If empty because of active filter (category or "resolved" tab), show a simpler "No threads found" message instead of the full educational empty state.

   e. **Render `<CreateThreadModal />` at the bottom** of PlanningView (it reads its own open/close state from uiStore).

   f. **Thread grid:** Keep existing `grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4`. Pass `categoryName` and `categoryEmoji` as new props to ThreadCard.
  </action>
  <verify>
    <automated>cd /home/jean-luc-makiola/Development/projects/GearBox && bun run lint 2>&1 | tail -5</automated>
  </verify>
  <done>PlanningView shows educational empty state with 3-step workflow, pill tabs for Active/Resolved, category filter dropdown, "New Thread" button opens modal, ThreadCard shows category badge, inline form is removed</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <files>src/client/routes/collection/index.tsx</files>
  <name>Task 3: Verify planning tab overhaul</name>
  <what-built>Complete planning tab overhaul: thread creation modal, educational empty state, Active/Resolved pill tabs, category filter, and category display on thread cards.</what-built>
  <how-to-verify>
    1. Start both dev servers: `bun run dev:server` and `bun run dev:client`
    2. Visit http://localhost:5173/collection?tab=planning
    3. Verify the educational empty state appears with 3 illustrated steps and a "Create your first thread" CTA button
    4. Click "Create your first thread" -- a modal should open with name input and category dropdown
    5. Create a thread (enter a name, select a category, submit)
    6. Verify the thread appears as a card with category emoji + name badge
    7. Verify the "New Thread" button appears in the header area
    8. Create a second thread in a different category
    9. Test the category filter dropdown -- filtering should show only matching threads
    10. Test the Active/Resolved pill tabs -- should toggle between active and resolved views
  </how-to-verify>
  <resume-signal>Type "approved" or describe issues</resume-signal>
  <action>Human verifies the planning tab UI overhaul by testing the complete flow in browser.</action>
  <verify>
    <automated>cd /home/jean-luc-makiola/Development/projects/GearBox && bun run lint 2>&1 | tail -5</automated>
  </verify>
  <done>User confirms: empty state shows 3-step workflow, modal creates threads with category, pill tabs filter Active/Resolved, category filter works, thread cards show category</done>
</task>

</tasks>

<verification>
- `bun run lint` passes with no errors
- Planning tab shows educational empty state when no threads exist
- Thread creation modal opens from both empty state CTA and header button
- Creating a thread with name + category succeeds and thread appears in list
- Thread cards show category emoji and name
- Active/Resolved pill tabs filter correctly
- Category filter narrows the thread list
</verification>

<success_criteria>
- Inline thread creation form is replaced with modal dialog
- Empty state educates users about the 3-step planning workflow
- Active/Resolved pill tabs replace the "Show archived" checkbox
- Category filter allows narrowing thread list by category
- Thread cards display category information
- No lint errors
</success_criteria>

<output>
After completion, create `.planning/phases/04-database-planning-fixes/04-02-SUMMARY.md`
</output>
