// src/hooks/useShoppingLists.tsx import { useState, useCallback, useEffect, useMemo } from 'react'; import { useAuth } from '../hooks/useAuth'; import { useUserData } from '../hooks/useUserData'; import { useCreateShoppingListMutation, useDeleteShoppingListMutation, useAddShoppingListItemMutation, useUpdateShoppingListItemMutation, useRemoveShoppingListItemMutation, } from './mutations'; import { logger } from '../services/logger.client'; import type { ShoppingListItem } from '../types'; /** * A custom hook to manage all state and logic related to shopping lists. * * This hook has been refactored to use TanStack Query mutations (ADR-0005 Phase 4). * It provides a simplified interface for shopping list operations with: * - Automatic cache invalidation * - Success/error notifications * - No manual state management * * The interface remains backward compatible with the previous implementation. */ const useShoppingListsHook = () => { const { userProfile } = useAuth(); const { shoppingLists } = useUserData(); // Local state for tracking the active list (UI concern, not server state) const [activeListId, setActiveListId] = useState(null); // TanStack Query mutation hooks const createListMutation = useCreateShoppingListMutation(); const deleteListMutation = useDeleteShoppingListMutation(); const addItemMutation = useAddShoppingListItemMutation(); const updateItemMutation = useUpdateShoppingListItemMutation(); const removeItemMutation = useRemoveShoppingListItemMutation(); // Consolidate errors from all mutations const error = useMemo(() => { const errors = [ createListMutation.error, deleteListMutation.error, addItemMutation.error, updateItemMutation.error, removeItemMutation.error, ]; const firstError = errors.find((err) => err !== null); return firstError?.message || null; }, [ createListMutation.error, deleteListMutation.error, addItemMutation.error, updateItemMutation.error, removeItemMutation.error, ]); // Effect to select the first list as active when lists are loaded or the user changes. useEffect(() => { // Check if the currently active list still exists in the shoppingLists array. const activeListExists = shoppingLists.some((l) => l.shopping_list_id === activeListId); // If the user is logged in and there are lists... if (userProfile && shoppingLists.length > 0) { // ...but no list is active, or the active one was deleted, select the first available list. if (!activeListExists) { setActiveListId(shoppingLists[0].shopping_list_id); } } else if (activeListId !== null) { // If there's no user or no lists, ensure no list is active. setActiveListId(null); } }, [shoppingLists, userProfile, activeListId]); /** * Create a new shopping list. * Uses TanStack Query mutation which automatically invalidates the cache. */ const createList = useCallback( async (name: string) => { if (!userProfile) return; try { await createListMutation.mutateAsync({ name }); } catch (error) { // Error is already handled by the mutation hook (notification shown) logger.error({ err: error }, '[useShoppingLists] Failed to create list'); } }, [userProfile, createListMutation], ); /** * Delete a shopping list. * Uses TanStack Query mutation which automatically invalidates the cache. */ const deleteList = useCallback( async (listId: number) => { if (!userProfile) return; try { await deleteListMutation.mutateAsync({ listId }); } catch (error) { // Error is already handled by the mutation hook (notification shown) logger.error({ err: error }, '[useShoppingLists] Failed to delete list'); } }, [userProfile, deleteListMutation], ); /** * Add an item to a shopping list. * Uses TanStack Query mutation which automatically invalidates the cache. * * Note: Duplicate checking has been moved to the server-side. * The API will handle duplicate detection and return appropriate errors. */ const addItemToList = useCallback( async (listId: number, item: { masterItemId?: number; customItemName?: string }) => { if (!userProfile) return; try { await addItemMutation.mutateAsync({ listId, item }); } catch (error) { // Error is already handled by the mutation hook (notification shown) logger.error({ err: error }, '[useShoppingLists] Failed to add item'); } }, [userProfile, addItemMutation], ); /** * Update a shopping list item (quantity, purchased status, notes, etc). * Uses TanStack Query mutation which automatically invalidates the cache. */ const updateItemInList = useCallback( async (itemId: number, updates: Partial) => { if (!userProfile) return; try { await updateItemMutation.mutateAsync({ itemId, updates }); } catch (error) { // Error is already handled by the mutation hook (notification shown) logger.error({ err: error }, '[useShoppingLists] Failed to update item'); } }, [userProfile, updateItemMutation], ); /** * Remove an item from a shopping list. * Uses TanStack Query mutation which automatically invalidates the cache. */ const removeItemFromList = useCallback( async (itemId: number) => { if (!userProfile) return; try { await removeItemMutation.mutateAsync({ itemId }); } catch (error) { // Error is already handled by the mutation hook (notification shown) logger.error({ err: error }, '[useShoppingLists] Failed to remove item'); } }, [userProfile, removeItemMutation], ); return { shoppingLists, activeListId, setActiveListId, createList, deleteList, addItemToList, updateItemInList, removeItemFromList, // Loading states from mutations isCreatingList: createListMutation.isPending, isDeletingList: deleteListMutation.isPending, isAddingItem: addItemMutation.isPending, isUpdatingItem: updateItemMutation.isPending, isRemovingItem: removeItemMutation.isPending, error, }; }; export { useShoppingListsHook as useShoppingLists };