Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
189 lines
6.2 KiB
TypeScript
189 lines
6.2 KiB
TypeScript
// 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<number | null>(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<ShoppingListItem>) => {
|
|
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 };
|