---
phase: 07-weight-unit-selection
plan: 02
type: execute
wave: 2
depends_on:
  - "07-01"
files_modified:
  - src/client/components/TotalsBar.tsx
  - src/client/components/ItemCard.tsx
  - src/client/components/CandidateCard.tsx
  - src/client/components/CategoryHeader.tsx
  - src/client/components/SetupCard.tsx
  - src/client/components/ItemPicker.tsx
  - src/client/routes/index.tsx
  - src/client/routes/setups/$setupId.tsx
autonomous: false
requirements:
  - UNIT-01
  - UNIT-02
  - UNIT-03

must_haves:
  truths:
    - "User can see a unit toggle (g/oz/lb/kg) in the TotalsBar"
    - "Clicking a unit in the toggle changes all weight displays across the app"
    - "Weight unit selection persists after page refresh"
    - "Every weight display in the app uses the selected unit"
  artifacts:
    - path: "src/client/components/TotalsBar.tsx"
      provides: "Unit toggle UI and unit-aware weight display"
      contains: "useWeightUnit"
    - path: "src/client/components/ItemCard.tsx"
      provides: "Unit-aware item weight display"
      contains: "useWeightUnit"
    - path: "src/client/components/CandidateCard.tsx"
      provides: "Unit-aware candidate weight display"
      contains: "useWeightUnit"
    - path: "src/client/components/CategoryHeader.tsx"
      provides: "Unit-aware category total weight display"
      contains: "useWeightUnit"
    - path: "src/client/components/SetupCard.tsx"
      provides: "Unit-aware setup weight display"
      contains: "useWeightUnit"
    - path: "src/client/components/ItemPicker.tsx"
      provides: "Unit-aware item picker weight display"
      contains: "useWeightUnit"
    - path: "src/client/routes/index.tsx"
      provides: "Unit-aware dashboard weight display"
      contains: "useWeightUnit"
    - path: "src/client/routes/setups/$setupId.tsx"
      provides: "Unit-aware setup detail weight display"
      contains: "useWeightUnit"
  key_links:
    - from: "src/client/components/TotalsBar.tsx"
      to: "/api/settings/weightUnit"
      via: "useUpdateSetting mutation"
      pattern: "useUpdateSetting.*weightUnit"
    - from: "src/client/components/ItemCard.tsx"
      to: "src/client/hooks/useWeightUnit.ts"
      via: "useWeightUnit hook import"
      pattern: "useWeightUnit"
    - from: "src/client/components/TotalsBar.tsx"
      to: "src/client/lib/formatters.ts"
      via: "formatWeight(grams, unit)"
      pattern: "formatWeight\\(.*,\\s*unit"
---

<objective>
Wire weight unit selection through the entire app: add a segmented unit toggle to TotalsBar and update all 8 formatWeight call sites to use the selected unit.

Purpose: Deliver the complete user-facing feature. After this plan, users can select g/oz/lb/kg and see all weights update instantly across collection, planning, setups, and dashboard.
Output: Fully functional weight unit selection with persistent preference.
</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/07-weight-unit-selection/07-RESEARCH.md
@.planning/phases/07-weight-unit-selection/07-01-SUMMARY.md
</context>

<interfaces>
<!-- Contracts created by Plan 01 that this plan consumes -->

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

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

From src/client/hooks/useSettings.ts (existing):
```typescript
export function useUpdateSetting(): UseMutationResult<Setting, Error, { key: string; value: string }>;
```

Usage pattern for every component:
```typescript
import { useWeightUnit } from "../hooks/useWeightUnit";
// ...
const unit = useWeightUnit();
// ...
{formatWeight(weightGrams, unit)}
```
</interfaces>

<tasks>

<task type="auto">
  <name>Task 1: Add unit toggle to TotalsBar and update all call sites</name>
  <files>
    src/client/components/TotalsBar.tsx,
    src/client/components/ItemCard.tsx,
    src/client/components/CandidateCard.tsx,
    src/client/components/CategoryHeader.tsx,
    src/client/components/SetupCard.tsx,
    src/client/components/ItemPicker.tsx,
    src/client/routes/index.tsx,
    src/client/routes/setups/$setupId.tsx
  </files>
  <action>
    **TotalsBar.tsx** -- Add unit toggle and wire formatWeight:

    1. Import `useWeightUnit` from `../hooks/useWeightUnit`, `useUpdateSetting` from `../hooks/useSettings`, and `WeightUnit` type from `../lib/formatters`
    2. Inside the component function, call `const unit = useWeightUnit()` and `const updateSetting = useUpdateSetting()`
    3. Define `const UNITS: WeightUnit[] = ["g", "oz", "lb", "kg"]`
    4. Add a segmented pill toggle to the right side of the TotalsBar, between the title and the stats. The toggle should be a `div` with `flex items-center gap-1 bg-gray-100 rounded-full px-1 py-0.5` containing a button per unit:
       ```
       <button
         key={u}
         onClick={() => updateSetting.mutate({ key: "weightUnit", value: u })}
         className={`px-2 py-0.5 text-xs rounded-full transition-colors ${
           unit === u
             ? "bg-white text-gray-700 shadow-sm font-medium"
             : "text-gray-400 hover:text-gray-600"
         }`}
       >
         {u}
       </button>
       ```
    5. Update the default stats construction (the `data?.global` branch) to pass `unit` to both `formatWeight` calls:
       - `formatWeight(data.global.totalWeight, unit)` and `formatWeight(null, unit)`
    6. Position the toggle: place it in the flex container between the title and stats, using a wrapper div that pushes stats to the right. The toggle should be visible but not dominant -- it's a small utility control.

    **ItemCard.tsx** -- 3-line change:
    1. Add import: `import { useWeightUnit } from "../hooks/useWeightUnit";`
    2. Inside component: `const unit = useWeightUnit();`
    3. Change `{formatWeight(weightGrams)}` to `{formatWeight(weightGrams, unit)}`

    **CandidateCard.tsx** -- Same 3-line pattern as ItemCard:
    1. Add import: `import { useWeightUnit } from "../hooks/useWeightUnit";`
    2. Inside component: `const unit = useWeightUnit();`
    3. Change `{formatWeight(weightGrams)}` to `{formatWeight(weightGrams, unit)}`

    **CategoryHeader.tsx** -- Same 3-line pattern:
    1. Add import: `import { useWeightUnit } from "../hooks/useWeightUnit";`
    2. Inside component: `const unit = useWeightUnit();`
    3. Change `{formatWeight(totalWeight)}` to `{formatWeight(totalWeight, unit)}`

    **SetupCard.tsx** -- Same 3-line pattern:
    1. Add import: `import { useWeightUnit } from "../hooks/useWeightUnit";`
    2. Inside component: `const unit = useWeightUnit();`
    3. Change `{formatWeight(totalWeight)}` to `{formatWeight(totalWeight, unit)}`

    **ItemPicker.tsx** -- Same 3-line pattern:
    1. Add import: `import { useWeightUnit } from "../hooks/useWeightUnit";`
    2. Inside component: `const unit = useWeightUnit();`
    3. Change `formatWeight(item.weightGrams)` to `formatWeight(item.weightGrams, unit)`

    **routes/index.tsx** (Dashboard) -- Same 3-line pattern:
    1. Add import: `import { useWeightUnit } from "../hooks/useWeightUnit";`
    2. Inside `DashboardPage`: `const unit = useWeightUnit();`
    3. Change `formatWeight(global?.totalWeight ?? null)` to `formatWeight(global?.totalWeight ?? null, unit)`

    **routes/setups/$setupId.tsx** (Setup Detail) -- Same 3-line pattern:
    1. Add import: `import { useWeightUnit } from "../../hooks/useWeightUnit";`
    2. Inside `SetupDetailPage`: `const unit = useWeightUnit();`
    3. Change `{formatWeight(totalWeight)}` to `{formatWeight(totalWeight, unit)}`

    **Completeness check:** After all changes, grep for `formatWeight(` across `src/client/` -- every call must have a second `unit` argument EXCEPT the function definition itself in `formatters.ts`.
  </action>
  <verify>
    <automated>bun test && bun run lint</automated>
  </verify>
  <done>
    - All 8 components pass `unit` to `formatWeight`
    - TotalsBar renders a g/oz/lb/kg toggle
    - Clicking a toggle button calls `useUpdateSetting` with key "weightUnit"
    - No `formatWeight` call site in src/client/ is missing the unit argument (except the definition)
    - All tests and lint pass
  </done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 2: Verify weight unit selection end-to-end</name>
  <action>
    Human verifies the complete weight unit selection feature works correctly across all pages.

    Start the dev servers: `bun run dev:client` and `bun run dev:server`
    Open http://localhost:5173 in a browser and walk through the verification steps below.
  </action>
  <verify>
    1. Navigate to the Collection page -- verify the TotalsBar shows a g/oz/lb/kg toggle
    2. The default should be "g" -- weights display as before (e.g., "450g")
    3. Click "oz" -- all weight badges on ItemCards, CategoryHeaders, and the TotalsBar total should update to ounces (e.g., "15.9 oz")
    4. Click "kg" -- weights should update to kilograms (e.g., "0.45 kg")
    5. Click "lb" -- weights should update to pounds (e.g., "0.99 lb")
    6. Navigate to the Dashboard (/) -- the Collection card weight should show in the selected unit
    7. Navigate to a Setup detail page -- the sticky sub-bar weight total and all ItemCards should show the selected unit
    8. Refresh the page -- the selected unit should persist (still showing the last chosen unit)
    9. Switch back to "g" -- all weights should return to the original gram display
  </verify>
  <done>User confirms all weight displays update correctly across all pages, unit toggle is visible and functional, and selection persists across refresh. Type "approved" or describe issues.</done>
</task>

</tasks>

<verification>
- `bun test` passes (full suite, no regressions)
- `bun run lint` passes
- grep `formatWeight(` across `src/client/` shows all call sites have unit parameter
- Unit toggle is visible in TotalsBar on all pages that show it
- Selecting a unit updates all weight displays instantly
- Selected unit persists across page refresh
</verification>

<success_criteria>
- UNIT-01: User can select g/oz/lb/kg from the TotalsBar toggle -- visible and functional
- UNIT-02: Every weight display (ItemCard, CandidateCard, CategoryHeader, SetupCard, ItemPicker, Dashboard, Setup Detail, TotalsBar) reflects the selected unit
- UNIT-03: Weight unit persists across sessions via the existing settings API (PUT/GET /api/settings/weightUnit)
</success_criteria>

<output>
After completion, create `.planning/phases/07-weight-unit-selection/07-02-SUMMARY.md`
</output>
