---
phase: 18-global-items-public-profiles
plan: 05
type: execute
wave: 3
depends_on: ["18-03"]
files_modified:
  - src/client/hooks/useProfile.ts
  - src/client/routes/users/$userId.tsx
  - src/client/routes/settings.tsx
  - src/client/routes/setups/index.tsx
  - src/client/components/ProfileSection.tsx
  - src/client/components/PublicSetupCard.tsx
autonomous: false
requirements: [PROF-01, PROF-02, PROF-03, PROF-04, PROF-05]

must_haves:
  truths:
    - "User can edit display name, avatar, and bio in settings page"
    - "Public profile page at /users/:id shows display name, avatar, bio, and public setups"
    - "Public profile page works without login (no auth required)"
    - "User can toggle a setup between public and private in the setup detail/edit view"
    - "Public setups appear on the owner's profile page; private ones do not"
  artifacts:
    - path: "src/client/hooks/useProfile.ts"
      provides: "usePublicProfile, useUpdateProfile hooks"
      exports: ["usePublicProfile", "useUpdateProfile"]
    - path: "src/client/routes/users/$userId.tsx"
      provides: "Public profile page"
      min_lines: 40
    - path: "src/client/components/ProfileSection.tsx"
      provides: "Profile edit form within settings"
      min_lines: 30
    - path: "src/client/components/PublicSetupCard.tsx"
      provides: "Card for setup shown on public profile"
      min_lines: 15
  key_links:
    - from: "src/client/routes/users/$userId.tsx"
      to: "src/client/hooks/useProfile.ts"
      via: "usePublicProfile hook"
      pattern: "usePublicProfile"
    - from: "src/client/hooks/useProfile.ts"
      to: "/api/users/:id/profile"
      via: "apiGet fetch"
      pattern: "apiGet.*users.*profile"
    - from: "src/client/routes/settings.tsx"
      to: "src/client/components/ProfileSection.tsx"
      via: "component import"
      pattern: "ProfileSection"
---

<objective>
Build the user profile and public sharing client: profile edit section in settings, public profile page, setup visibility toggle, and public setup cards.

Purpose: Delivers the client-side experience for PROF-01 (profile edit), PROF-02 (public profile), PROF-03 (setup toggle), PROF-04 (public setup view), PROF-05 (profile lists public setups).
Output: useProfile hook, public profile page, ProfileSection component, PublicSetupCard, updated settings and setup views
</objective>

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

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/18-global-items-public-profiles/18-CONTEXT.md
@.planning/phases/18-global-items-public-profiles/18-RESEARCH.md
@.planning/phases/18-global-items-public-profiles/18-03-SUMMARY.md

@src/client/routes/settings.tsx
@src/client/routes/setups/index.tsx
@src/client/hooks/useItems.ts
@src/client/lib/api.ts

<interfaces>
<!-- API endpoints from Plan 03: -->
PUT /api/auth/profile { displayName?, avatarUrl?, bio? } -> updated user (auth required)
GET /api/users/:id/profile -> { id, displayName, avatarUrl, bio, setups: [{ id, name, createdAt }] } (no auth)
GET /api/setups/:id/public -> { id, name, isPublic, items: [...], totalWeight, totalCost } (no auth, 404 if private)

<!-- Setup now includes isPublic in responses from Plan 03 -->
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Profile hooks and profile edit UI</name>
  <files>src/client/hooks/useProfile.ts, src/client/components/ProfileSection.tsx, src/client/routes/settings.tsx</files>
  <read_first>src/client/routes/settings.tsx, src/client/hooks/useItems.ts, src/client/lib/api.ts</read_first>
  <action>
**useProfile.ts** hook: Create at `src/client/hooks/useProfile.ts`.

1. `usePublicProfile(userId: number | null)` — `useQuery` with key `["profiles", userId]`, fetches `apiGet("/api/users/${userId}/profile")`, `enabled: userId != null`.

2. `useUpdateProfile()` — `useMutation` calling `apiPut("/api/auth/profile", data)`. On success, invalidate `["profiles"]` query key. Return mutation.

**ProfileSection.tsx**: Create at `src/client/components/ProfileSection.tsx`. Per D-09.

A form section that contains:
- Display name text input (max 100 chars) with label
- Bio textarea (max 500 chars) with character counter
- Avatar: Show current avatar if set, with a "Change avatar" button that opens the existing ImageUpload component (per D-11, reuse existing image upload + MinIO storage). After upload, set avatarUrl to the returned filename (the route will handle presigned URL generation).
- Save button calling `useUpdateProfile()` mutation
- Success/error toast feedback (use existing toast pattern if available, otherwise simple inline message)

Pre-populate form with current profile data. On mount, fetch current user profile via an appropriate mechanism (could be from auth context or a dedicated endpoint).

**settings.tsx**: Read the existing settings page. Add a "Profile" section at the top (before API Keys and other settings). Import and render `<ProfileSection />`. The section should have a heading "Profile" with a brief description "Your public profile information."
  </action>
  <verify>
    <automated>bun run lint 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - grep -q "usePublicProfile" src/client/hooks/useProfile.ts
    - grep -q "useUpdateProfile" src/client/hooks/useProfile.ts
    - grep -q "ProfileSection" src/client/components/ProfileSection.tsx
    - grep -q "ProfileSection" src/client/routes/settings.tsx
  </acceptance_criteria>
  <done>Profile edit section in settings page with display name, bio, and avatar upload. Hooks handle fetch and mutation. Form saves correctly.</done>
</task>

<task type="auto">
  <name>Task 2: Public profile page and setup visibility toggle</name>
  <files>src/client/routes/users/$userId.tsx, src/client/components/PublicSetupCard.tsx, src/client/routes/setups/index.tsx</files>
  <read_first>src/client/routes/setups/index.tsx, src/client/hooks/useProfile.ts</read_first>
  <action>
**users/$userId.tsx**: Create at `src/client/routes/users/$userId.tsx`. Per D-10.

Public profile page (no auth required to view):
- TanStack Router: `createFileRoute("/users/$userId")` with params
- Fetch profile with `usePublicProfile(Number(userId))`
- Layout: Avatar (or placeholder icon), display name (or "User #{id}" fallback), bio text
- Below profile: "Public Setups" heading with grid of PublicSetupCard components
- Empty state if no public setups: "No public setups yet"
- Loading skeleton while fetching
- 404 handling if user not found

**PublicSetupCard.tsx**: Create at `src/client/components/PublicSetupCard.tsx`.

A card for setups shown on the public profile:
- Setup name as heading
- Created date formatted
- Links to `/setups/${id}/public` for the public view (or you can create an inline expandable view)
- Light card styling with subtle border/shadow, matching existing setup cards

**setups/index.tsx or setup detail**: Update the setup list or detail view to include the isPublic toggle per D-14.

- In the setup detail/edit view, add a toggle switch or checkbox labeled "Public" next to the setup name
- When toggled, call the existing setup update mutation with `isPublic: true/false`
- Show a small icon or badge on the setup list indicating public status (e.g., a globe icon or "Public" chip)
- Default all existing setups to show as private (per D-12)
  </action>
  <verify>
    <automated>bun run lint 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - test -f "src/client/routes/users/\$userId.tsx"
    - grep -q "usePublicProfile" "src/client/routes/users/\$userId.tsx"
    - test -f src/client/components/PublicSetupCard.tsx
    - grep -q "isPublic\|public" src/client/routes/setups/index.tsx
  </acceptance_criteria>
  <done>Public profile page shows user info and public setups. Setup detail has visibility toggle. Public setups appear on profile. Private setups are hidden from profile.</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 3: Verify profiles and public sharing UI</name>
  <files>none</files>
  <action>
Human verification of user profiles and public sharing. Review what was built: profile edit in settings, public profile page, setup visibility toggle.

Steps to verify:
1. Start dev server: `bun run dev`
2. Go to Settings — should see a new "Profile" section at top
3. Enter a display name and bio, save — should show success
4. Upload an avatar image — should display
5. Go to Setups, open a setup detail, find the "Public" toggle
6. Toggle a setup to public
7. Navigate to `/users/{your-user-id}` — should see profile with the public setup listed
8. Open an incognito/private window (no auth)
9. Visit the same `/users/{id}` URL — should show profile and public setup without login
10. Toggle the setup back to private — it should disappear from the profile page
  </action>
  <verify>
    <automated>bun run build 2>&1 | tail -3</automated>
  </verify>
  <done>User approves profiles and sharing UI: profile edit works, public profile shows correct data, setup toggle works, unauthenticated access functions correctly.</done>
</task>

</tasks>

<verification>
- `bun run lint` passes
- `bun run build` succeeds
- Visual verification: profile edit, public profile page, setup toggle, and public access
</verification>

<success_criteria>
Profile can be edited in settings. Public profile page works without auth. Setup visibility toggle works. Public setups appear on profile, private ones don't. Avatar upload uses existing image infrastructure.
</success_criteria>

<output>
After completion, create `.planning/phases/18-global-items-public-profiles/18-05-SUMMARY.md`
</output>
