Refactor useWatchedItems hook to utilize useApi for API calls, update tests accordingly
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 18m15s

- Replaced direct API calls in useWatchedItems with useApi hook for add and remove watched items.
- Updated tests for useWatchedItems to mock useApi and verify API interactions.
- Consolidated error handling for API calls in useWatchedItems.
- Adjusted related hooks and components to ensure compatibility with the new structure.
- Added new ScaleIcon component for UI consistency.
- Implemented useFlyerItems and useFlyers hooks for fetching flyer data.
- Created useMasterItems and useUserData hooks for managing master grocery items and user data.
- Developed useInfiniteQuery hook for handling paginated API responses.
- Added comprehensive tests for useApi and useInfiniteQuery hooks.
- Introduced comparePrices API endpoint and corresponding client-side functionality.
This commit is contained in:
2025-12-13 08:57:15 -08:00
parent 117f034b2b
commit 728f4a5f7e
37 changed files with 1373 additions and 847 deletions

View File

@@ -1,7 +1,8 @@
// src/hooks/useWatchedItems.tsx
import { useState, useCallback } from 'react';
import { useMemo, useCallback } from 'react';
import { useAuth } from './useAuth';
import { useData } from './useData';
import { useApi } from './useApi';
import { useUserData } from './useUserData';
import * as apiClient from '../services/apiClient';
import type { MasterGroceryItem } from '../types';
import { logger } from '../services/logger.client';
@@ -13,14 +14,24 @@ import { logger } from '../services/logger.client';
export const useWatchedItems = () => {
const { user } = useAuth();
// Get the watched items and the global setter from the DataContext.
const { watchedItems, setWatchedItems } = useData();
const [error, setError] = useState<string | null>(null);
const { watchedItems, setWatchedItems } = useUserData();
// API hooks for watched item operations
const { execute: addWatchedItemApi, error: addError } = useApi<MasterGroceryItem, [string, string]>(
(itemName, category) => apiClient.addWatchedItem(itemName, category)
);
const { execute: removeWatchedItemApi, error: removeError } = useApi<null, [number]>(
(masterItemId) => apiClient.removeWatchedItem(masterItemId)
);
// Consolidate errors into a single displayable error message.
const error = useMemo(() => (addError || removeError ? (addError?.message || removeError?.message) : null), [addError, removeError]);
const addWatchedItem = useCallback(async (itemName: string, category: string) => {
if (!user) return;
try {
setError(null);
const updatedOrNewItem: MasterGroceryItem = await (await apiClient.addWatchedItem(itemName, category)).json();
const updatedOrNewItem = await addWatchedItemApi(itemName, category);
if (updatedOrNewItem) {
// Update the global state in the DataContext.
setWatchedItems(currentItems => {
@@ -30,27 +41,18 @@ export const useWatchedItems = () => {
}
return currentItems;
});
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
logger.error('Could not add watched item', { error: errorMessage });
setError(`Could not add watched item: ${errorMessage}`);
}
}, [user, setWatchedItems]);
}, [user, setWatchedItems, addWatchedItemApi]);
const removeWatchedItem = useCallback(async (masterItemId: number) => {
if (!user) return;
try {
setError(null);
await apiClient.removeWatchedItem(masterItemId);
const result = await removeWatchedItemApi(masterItemId);
if (result === null) {
// Update the global state in the DataContext.
setWatchedItems(currentItems => currentItems.filter(item => item.master_grocery_item_id !== masterItemId));
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
logger.error('Could not remove watched item', { error: errorMessage });
setError(`Could not remove watched item: ${errorMessage}`);
}
}, [user, setWatchedItems]);
}, [user, setWatchedItems, removeWatchedItemApi]);
return {
watchedItems,