All files / web/src/hooks useHousehold.ts

0% Statements 0/179
0% Branches 0/1
0% Functions 0/1
0% Lines 0/179

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180                                                                                                                                                                                                                                                                                                                                                                       
'use client'

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { api } from '@/lib/queryClient'
import { householdKeys } from '@/lib/queryKeys'

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

interface HouseholdSummary {
  id: string
  name: string
  ownerId: string
  role: 'owner' | 'member'
  memberCount: number
}

interface HouseholdMember {
  userId: string
  name: string | null
  email: string | null
  image: string | null
  role: 'owner' | 'member'
  joinedAt: string
}

interface HouseholdDetail {
  id: string
  name: string
  ownerId: string
  createdAt: string
  members: HouseholdMember[]
}

export interface HouseholdSuggestion {
  userId: string
  name: string | null
  email: string | null
  image: string | null
  /** Names of shared children */
  sharedChildren: string[]
}

// ---------------------------------------------------------------------------
// API helpers
// ---------------------------------------------------------------------------

async function fetchHouseholds(): Promise<{ households: HouseholdSummary[] }> {
  const res = await api('households')
  if (!res.ok) throw new Error('Failed to fetch households')
  return res.json()
}

async function fetchHouseholdDetail(
  id: string
): Promise<{ household: HouseholdDetail; suggestions: HouseholdSuggestion[] }> {
  const res = await api(`households/${id}`)
  if (!res.ok) throw new Error('Failed to fetch household')
  return res.json()
}

async function createHouseholdApi(
  name: string
): Promise<{ household: { id: string; name: string } }> {
  const res = await api('households', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name }),
  })
  if (!res.ok) {
    const data = await res.json().catch(() => ({}))
    throw new Error(data.error || 'Failed to create household')
  }
  return res.json()
}

async function addMemberApi(householdId: string, params: { userId?: string; email?: string }) {
  const res = await api(`households/${householdId}/members`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params),
  })
  if (!res.ok) {
    const data = await res.json().catch(() => ({}))
    throw new Error(data.error || 'Failed to add member')
  }
  return res.json()
}

async function removeMemberApi(householdId: string, userId: string) {
  const res = await api(`households/${householdId}/members/${userId}`, {
    method: 'DELETE',
  })
  if (!res.ok) {
    const data = await res.json().catch(() => ({}))
    throw new Error(data.error || 'Failed to remove member')
  }
  return res.json()
}

async function updateHouseholdApi(householdId: string, body: Record<string, unknown>) {
  const res = await api(`households/${householdId}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  })
  if (!res.ok) {
    const data = await res.json().catch(() => ({}))
    throw new Error(data.error || 'Failed to update household')
  }
  return res.json()
}

// ---------------------------------------------------------------------------
// Hooks
// ---------------------------------------------------------------------------

/** List the current user's households */
export function useHouseholds() {
  return useQuery({
    queryKey: householdKeys.list(),
    queryFn: fetchHouseholds,
    select: (data) => data.households,
  })
}

/** Get a single household with full member details and suggestions */
export function useHousehold(id: string | undefined) {
  return useQuery({
    queryKey: id ? householdKeys.detail(id) : householdKeys.list(),
    queryFn: () => (id ? fetchHouseholdDetail(id) : Promise.reject('No id')),
    enabled: !!id,
    select: (data) => ({ ...data.household, suggestions: data.suggestions }),
  })
}

/** Mutations for household management */
export function useHouseholdMutations() {
  const queryClient = useQueryClient()

  const invalidateAll = () => {
    queryClient.invalidateQueries({ queryKey: householdKeys.all })
  }

  const create = useMutation({
    mutationFn: (name: string) => createHouseholdApi(name),
    onSuccess: invalidateAll,
  })

  const addMember = useMutation({
    mutationFn: (params: { householdId: string; userId?: string; email?: string }) =>
      addMemberApi(params.householdId, {
        userId: params.userId,
        email: params.email,
      }),
    onSuccess: invalidateAll,
  })

  const removeMember = useMutation({
    mutationFn: (params: { householdId: string; userId: string }) =>
      removeMemberApi(params.householdId, params.userId),
    onSuccess: invalidateAll,
  })

  const rename = useMutation({
    mutationFn: (params: { householdId: string; name: string }) =>
      updateHouseholdApi(params.householdId, { name: params.name }),
    onSuccess: invalidateAll,
  })

  const transferOwnership = useMutation({
    mutationFn: (params: { householdId: string; newOwnerId: string }) =>
      updateHouseholdApi(params.householdId, { newOwnerId: params.newOwnerId }),
    onSuccess: invalidateAll,
  })

  return { create, addMember, removeMember, rename, transferOwnership }
}