---
phase: 09-weight-classification-and-visualization
plan: 02
type: execute
wave: 2
depends_on: ["09-01"]
files_modified:
  - src/client/components/WeightSummaryCard.tsx
  - src/client/routes/setups/$setupId.tsx
  - package.json
autonomous: false
requirements: [CLAS-02, VIZZ-01, VIZZ-02, VIZZ-03]

must_haves:
  truths:
    - "Setup detail view shows separate weight subtotals for base weight, worn weight, consumable weight, and total"
    - "User can view a donut chart showing weight distribution by category in the setup"
    - "User can toggle the chart between category breakdown and classification breakdown via pill toggle"
    - "Hovering a chart segment shows category/classification name, weight in selected unit, and percentage"
    - "Total weight displayed in the center of the donut hole"
  artifacts:
    - path: "src/client/components/WeightSummaryCard.tsx"
      provides: "Summary card with weight subtotals, donut chart, pill toggle, and tooltips"
      min_lines: 100
    - path: "src/client/routes/setups/$setupId.tsx"
      provides: "WeightSummaryCard rendered below sticky bar when setup has items"
    - path: "package.json"
      provides: "recharts dependency installed"
      contains: "recharts"
  key_links:
    - from: "src/client/components/WeightSummaryCard.tsx"
      to: "recharts"
      via: "PieChart, Pie, Cell, Tooltip, Label, ResponsiveContainer imports"
      pattern: "from.*recharts"
    - from: "src/client/components/WeightSummaryCard.tsx"
      to: "src/client/lib/formatters.ts"
      via: "formatWeight for subtotals and tooltip display"
      pattern: "formatWeight"
    - from: "src/client/routes/setups/$setupId.tsx"
      to: "src/client/components/WeightSummaryCard.tsx"
      via: "WeightSummaryCard rendered with setup.items prop"
      pattern: "WeightSummaryCard"
---

<objective>
Add the WeightSummaryCard component with classification weight subtotals, a donut chart for weight distribution, and a pill toggle for switching between category and classification views.

Purpose: Users need to visualize how weight is distributed across their setup -- both by gear category (shelter, sleep, cook) and by classification (base weight, worn, consumable). The donut chart with tooltips makes weight analysis intuitive.

Output: A summary card below the setup sticky bar showing Base | Worn | Consumable | Total weight columns alongside a donut chart with interactive tooltips, togglable between category and classification breakdowns.
</objective>

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

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/09-weight-classification-and-visualization/09-CONTEXT.md
@.planning/phases/09-weight-classification-and-visualization/09-RESEARCH.md
@.planning/phases/09-weight-classification-and-visualization/09-01-SUMMARY.md

<interfaces>
<!-- Key types and contracts from Plan 01. Executor uses these directly. -->

From src/client/hooks/useSetups.ts (after Plan 01):
```typescript
interface SetupItemWithCategory {
  id: number;
  name: string;
  weightGrams: number | null;
  priceCents: number | null;
  categoryId: number;
  notes: string | null;
  productUrl: string | null;
  imageFilename: string | null;
  createdAt: string;
  updatedAt: string;
  categoryName: string;
  categoryIcon: string;
  classification: string;  // "base" | "worn" | "consumable" -- added by Plan 01
}
```

From src/client/lib/formatters.ts:
```typescript
export type WeightUnit = "g" | "oz" | "lb" | "kg";
export function formatWeight(grams: number | null | undefined, unit: WeightUnit = "g"): string;
```

From src/client/hooks/useWeightUnit.ts:
```typescript
export function useWeightUnit(): WeightUnit;
```

From 09-RESEARCH.md (Recharts pattern):
```typescript
import { PieChart, Pie, Cell, Tooltip, Label, ResponsiveContainer } from "recharts";
// Use Cell for per-slice colors (still functional in v3, deprecated for v4)
// Use fixed numeric height on ResponsiveContainer (e.g., height={200})
// Filter out zero-weight entries before passing to chart
```

From 09-RESEARCH.md (color palettes):
```typescript
const CATEGORY_COLORS = [
  "#6366f1", "#f59e0b", "#10b981", "#ef4444", "#8b5cf6",
  "#06b6d4", "#f97316", "#ec4899", "#14b8a6", "#84cc16",
];
const CLASSIFICATION_COLORS = {
  base: "#6366f1",       // indigo
  worn: "#f59e0b",       // amber
  consumable: "#10b981", // emerald
};
```

From 09-CONTEXT.md (locked decisions):
- Summary card below sticky bar, always visible when setup has items
- Card with columns layout: Base | Worn | Consumable | Total
- Donut chart inside the summary card alongside weight subtotals
- Pill toggle above the chart: "Category" / "Classification" (same style as weight unit selector)
- Total weight in center of donut hole
- Hover tooltips: segment name, weight in selected unit, percentage
- Chart library: Recharts (PieChart + Pie with innerRadius)
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Install Recharts, create WeightSummaryCard, wire into setup detail page</name>
  <files>
    src/client/components/WeightSummaryCard.tsx,
    src/client/routes/setups/$setupId.tsx,
    package.json
  </files>
  <action>
    1. **Install Recharts**: Run `bun add recharts`. This adds recharts to package.json. React and react-dom are already peer deps in the project.

    2. **Create WeightSummaryCard component** (`src/client/components/WeightSummaryCard.tsx`):

       **Props interface:**
       ```typescript
       interface WeightSummaryCardProps {
         items: SetupItemWithCategory[];  // from useSetups hook (includes classification field)
       }
       ```
       Import `SetupItemWithCategory` from `../hooks/useSetups`.

       **State:** `viewMode: "category" | "classification"` -- local React state, default "category".

       **Weight subtotals computation** (derive from items array):
       ```typescript
       const baseWeight = items.reduce((sum, i) => i.classification === "base" ? sum + (i.weightGrams ?? 0) : sum, 0);
       const wornWeight = items.reduce((sum, i) => i.classification === "worn" ? sum + (i.weightGrams ?? 0) : sum, 0);
       const consumableWeight = items.reduce((sum, i) => i.classification === "consumable" ? sum + (i.weightGrams ?? 0) : sum, 0);
       const totalWeight = baseWeight + wornWeight + consumableWeight;
       ```

       **Chart data transformation:**
       - `buildCategoryChartData(items)`: Group by `categoryName`, sum `weightGrams`, compute percentage. Filter out zero-weight groups. Return `Array<{ name: string, weight: number, percent: number }>`.
       - `buildClassificationChartData(items)`: Group by classification using labels ("Base Weight", "Worn", "Consumable"), sum weights, compute percentage. Filter out zero-weight groups.
       - Select data source based on `viewMode`.

       **Render structure:**
       ```
       <div className="bg-white rounded-xl border border-gray-100 p-5 mb-6">
         <!-- Pill toggle: Category | Classification -->
         <div className="flex items-center justify-between mb-4">
           <h3 className="text-sm font-medium text-gray-700">Weight Summary</h3>
           <PillToggle viewMode={viewMode} onChange={setViewMode} />
         </div>

         <!-- Main content: chart + subtotals side by side -->
         <div className="flex items-center gap-8">
           <!-- Donut chart -->
           <div className="flex-shrink-0" style={{ width: 180, height: 180 }}>
             <ResponsiveContainer width="100%" height={180}>
               <PieChart>
                 <Pie data={chartData} dataKey="weight" nameKey="name"
                   cx="50%" cy="50%" innerRadius={55} outerRadius={80} paddingAngle={2}>
                   {chartData.map((entry, index) => (
                     <Cell key={entry.name} fill={colors[index % colors.length]} />
                   ))}
                   <Label value={formatWeight(totalWeight, unit)} position="center"
                     style={{ fontSize: "14px", fontWeight: 600, fill: "#374151" }} />
                 </Pie>
                 <Tooltip content={<CustomTooltip unit={unit} />} />
               </PieChart>
             </ResponsiveContainer>
           </div>

           <!-- Weight subtotals columns -->
           <div className="flex-1 grid grid-cols-4 gap-4">
             <SubtotalColumn label="Base" weight={baseWeight} unit={unit} color="#6366f1" />
             <SubtotalColumn label="Worn" weight={wornWeight} unit={unit} color="#f59e0b" />
             <SubtotalColumn label="Consumable" weight={consumableWeight} unit={unit} color="#10b981" />
             <SubtotalColumn label="Total" weight={totalWeight} unit={unit} />
           </div>
         </div>
       </div>
       ```

       **Pill toggle** (inline component or extracted):
       - Two buttons in a `bg-gray-100 rounded-full` container: "Category" and "Classification".
       - Active state: `bg-white text-gray-700 shadow-sm font-medium`. Inactive: `text-gray-400 hover:text-gray-600`.
       - Same pattern as TotalsBar weight unit selector.

       **SubtotalColumn** (inline component):
       - Vertical stack: colored dot (if color provided), label in text-xs text-gray-500, weight value in text-sm font-semibold text-gray-900.

       **CustomTooltip:**
       - Props: `active`, `payload`, `unit` (WeightUnit).
       - When active and payload exists, show: segment name (bold), weight formatted with `formatWeight()`, percentage as `(XX.X%)`.
       - Styled: `bg-white border border-gray-200 rounded-lg shadow-lg px-3 py-2 text-sm`.

       **Color selection:**
       - When `viewMode === "category"`: use `CATEGORY_COLORS` array (cycle through for many categories).
       - When `viewMode === "classification"`: use `CLASSIFICATION_COLORS` object (keyed by classification value).

       **Edge cases:**
       - If all items have null/zero weight, show a placeholder message ("No weight data to display") instead of the chart.
       - If items array is empty, component should not render (handled by parent).

    3. **Wire into setup detail page** (`src/client/routes/setups/$setupId.tsx`):
       - Import `WeightSummaryCard` from `../../components/WeightSummaryCard`.
       - Render `<WeightSummaryCard items={setup.items} />` between the actions bar and the items grid (before the `{itemCount > 0 && (` block), but INSIDE the `itemCount > 0` condition so it only shows when there are items.
       - Exact placement: after the actions `<div>` and before the items-grouped-by-category `<div>`, within the `{itemCount > 0 && (...)}` block.

    4. **Verify**: Run `bun run build` to ensure no TypeScript errors and Recharts imports resolve correctly.
  </action>
  <verify>
    <automated>bun run build</automated>
  </verify>
  <done>
    - WeightSummaryCard renders below sticky bar when setup has items
    - Shows 4 columns: Base | Worn | Consumable | Total with correct weight values in selected unit
    - Donut chart renders with colored segments for weight distribution
    - Pill toggle switches between category view and classification view
    - Hovering chart segments shows tooltip with name, weight, and percentage
    - Total weight displayed in center of donut hole
    - Empty/zero-weight items handled gracefully
    - Build succeeds with no TypeScript errors
  </done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 2: Visual verification of complete weight classification and visualization</name>
  <files>N/A</files>
  <action>
    Present the user with verification steps for the complete Phase 9 feature set.
    This checkpoint covers both Plan 01 (classification badges) and Plan 02 (summary card + chart) together.
  </action>
  <what-built>
    Complete weight classification and visualization system:
    1. Classification badges on every item card in setup view (click to cycle: base weight / worn / consumable)
    2. Weight summary card with Base | Worn | Consumable | Total subtotals
    3. Donut chart with category/classification toggle and hover tooltips
    4. Total weight in the center of the donut hole
  </what-built>
  <how-to-verify>
    1. Start dev servers: `bun run dev:server` and `bun run dev:client`
    2. Open http://localhost:5173 and navigate to a setup with items (or create one and add items)
    3. **Classification badges**: Verify each item card shows a gray pill badge. Click it and confirm it cycles: "Base Weight" -> "Worn" -> "Consumable" -> "Base Weight". Confirm clicking the badge does NOT open the item edit panel.
    4. **Classification persistence**: Refresh the page. Confirm classifications are preserved.
    5. **Weight subtotals**: With items classified differently, verify the summary card shows correct subtotals for Base, Worn, Consumable, and Total columns.
    6. **Donut chart (Category view)**: Verify the donut chart shows colored segments grouped by category. Hover segments to see tooltip with category name, weight, and percentage.
    7. **Donut chart (Classification view)**: Click the "Classification" pill toggle. Verify chart segments change to show base/worn/consumable breakdown with different colors. Hover to verify tooltips.
    8. **Donut center**: Confirm total weight is displayed in the center of the donut hole in the selected weight unit.
    9. **Weight unit**: Toggle the weight unit in the top bar (if available). Confirm all subtotals, chart center, and tooltips update to the new unit.
    10. **Add/remove items**: Add another item to the setup. Verify it appears with default "Base Weight" badge and the chart updates. Remove an item and verify classifications for remaining items are preserved.
  </how-to-verify>
  <verify>Visual verification by user following steps above</verify>
  <done>User confirms all classification badges, weight subtotals, donut chart, toggle, and tooltips work correctly</done>
  <resume-signal>Type "approved" to complete Phase 9, or describe any issues to address</resume-signal>
</task>

</tasks>

<verification>
```bash
# Full test suite passes
bun test

# Build succeeds
bun run build

# Lint passes
bun run lint
```
</verification>

<success_criteria>
- WeightSummaryCard visible below sticky bar on setup detail page (only when items exist)
- Four weight columns (Base, Worn, Consumable, Total) show correct values in selected unit
- Donut chart renders with colored segments proportional to weight distribution
- Pill toggle switches between category and classification chart views
- Tooltip on hover shows segment name, formatted weight, and percentage
- Total weight displayed in center of donut hole
- Chart handles edge cases (no weight data, single category, etc.)
- User confirms visual appearance matches expectations
</success_criteria>

<output>
After completion, create `.planning/phases/09-weight-classification-and-visualization/09-02-SUMMARY.md`
</output>
