complete project using prettier!

This commit is contained in:
2025-12-22 09:45:14 -08:00
parent 621d30b84f
commit a10f84aa48
339 changed files with 18041 additions and 8969 deletions

View File

@@ -14,36 +14,51 @@ const useShoppingListsHook = () => {
const { userProfile } = useAuth();
// We get the lists and the global setter from the DataContext.
const { shoppingLists, setShoppingLists } = useUserData();
const [activeListId, setActiveListId] = useState<number | null>(null);
// API hooks for shopping list operations
const { execute: createListApi, error: createError, loading: isCreatingList } = useApi<ShoppingList, [string]>(
(name) => apiClient.createShoppingList(name)
const {
execute: createListApi,
error: createError,
loading: isCreatingList,
} = useApi<ShoppingList, [string]>((name) => apiClient.createShoppingList(name));
const {
execute: deleteListApi,
error: deleteError,
loading: isDeletingList,
} = useApi<null, [number]>((listId) => apiClient.deleteShoppingList(listId));
const {
execute: addItemApi,
error: addItemError,
loading: isAddingItem,
} = useApi<ShoppingListItem, [number, { masterItemId?: number; customItemName?: string }]>(
(listId, item) => apiClient.addShoppingListItem(listId, item),
);
const { execute: deleteListApi, error: deleteError, loading: isDeletingList } = useApi<null, [number]>(
(listId) => apiClient.deleteShoppingList(listId)
);
const { execute: addItemApi, error: addItemError, loading: isAddingItem } = useApi<ShoppingListItem, [number, { masterItemId?: number, customItemName?: string }]>(
(listId, item) => apiClient.addShoppingListItem(listId, item)
);
const { execute: updateItemApi, error: updateItemError, loading: isUpdatingItem } = useApi<ShoppingListItem, [number, Partial<ShoppingListItem>]>(
(itemId, updates) => apiClient.updateShoppingListItem(itemId, updates)
);
const { execute: removeItemApi, error: removeItemError, loading: isRemovingItem } = useApi<null, [number]>(
(itemId) => apiClient.removeShoppingListItem(itemId)
const {
execute: updateItemApi,
error: updateItemError,
loading: isUpdatingItem,
} = useApi<ShoppingListItem, [number, Partial<ShoppingListItem>]>((itemId, updates) =>
apiClient.updateShoppingListItem(itemId, updates),
);
const {
execute: removeItemApi,
error: removeItemError,
loading: isRemovingItem,
} = useApi<null, [number]>((itemId) => apiClient.removeShoppingListItem(itemId));
// Consolidate errors from all API hooks into a single displayable error.
const error = useMemo(() => {
const firstError = createError || deleteError || addItemError || updateItemError || removeItemError;
const firstError =
createError || deleteError || addItemError || updateItemError || removeItemError;
return firstError ? firstError.message : null;
}, [createError, deleteError, addItemError, updateItemError, removeItemError]);
// 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);
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) {
@@ -57,103 +72,133 @@ const useShoppingListsHook = () => {
}
}, [shoppingLists, userProfile]); // This effect should NOT depend on activeListId to prevent re-selection loops.
const createList = useCallback(async (name: string) => {
if (!userProfile) return;
try {
const newList = await createListApi(name);
if (newList) {
setShoppingLists(prev => [...prev, newList]);
const createList = useCallback(
async (name: string) => {
if (!userProfile) return;
try {
const newList = await createListApi(name);
if (newList) {
setShoppingLists((prev) => [...prev, newList]);
}
} catch (e) {
// The useApi hook handles setting the error state.
// We catch the error here to prevent unhandled promise rejections and add logging.
console.error('useShoppingLists: Failed to create list.', e);
}
} catch (e) {
// The useApi hook handles setting the error state.
// We catch the error here to prevent unhandled promise rejections and add logging.
console.error('useShoppingLists: Failed to create list.', e);
}
}, [userProfile, setShoppingLists, createListApi]);
},
[userProfile, setShoppingLists, createListApi],
);
const deleteList = useCallback(async (listId: number) => {
if (!userProfile) return;
try {
const result = await deleteListApi(listId);
// A successful DELETE will have a null result from useApi (for 204 No Content)
if (result === null) {
setShoppingLists(prevLists => prevLists.filter(l => l.shopping_list_id !== listId));
const deleteList = useCallback(
async (listId: number) => {
if (!userProfile) return;
try {
const result = await deleteListApi(listId);
// A successful DELETE will have a null result from useApi (for 204 No Content)
if (result === null) {
setShoppingLists((prevLists) => prevLists.filter((l) => l.shopping_list_id !== listId));
}
} catch (e) {
console.error('useShoppingLists: Failed to delete list.', e);
}
} catch (e) {
console.error('useShoppingLists: Failed to delete list.', e);
}
}, [userProfile, setShoppingLists, deleteListApi]);
},
[userProfile, setShoppingLists, deleteListApi],
);
const addItemToList = useCallback(async (listId: number, item: { masterItemId?: number, customItemName?: string }) => {
if (!userProfile) return;
const addItemToList = useCallback(
async (listId: number, item: { masterItemId?: number; customItemName?: string }) => {
if (!userProfile) return;
// Find the target list first to check for duplicates *before* the API call.
const targetList = shoppingLists.find(l => l.shopping_list_id === listId);
if (!targetList) {
console.error(`useShoppingLists: List with ID ${listId} not found.`);
return; // Or throw an error
}
// Prevent adding a duplicate master item.
if (item.masterItemId) {
const itemExists = targetList.items.some(i => i.master_item_id === item.masterItemId);
if (itemExists) {
// Optionally, we could show a toast notification here.
console.log(`useShoppingLists: Item with master ID ${item.masterItemId} already in list.`);
return; // Exit without calling the API.
// Find the target list first to check for duplicates *before* the API call.
const targetList = shoppingLists.find((l) => l.shopping_list_id === listId);
if (!targetList) {
console.error(`useShoppingLists: List with ID ${listId} not found.`);
return; // Or throw an error
}
}
try {
const newItem = await addItemApi(listId, item);
if (newItem) {
setShoppingLists(prevLists =>
prevLists.map(list => {
if (list.shopping_list_id === listId) {
// The duplicate check is now handled above, so we can just add the item.
return { ...list, items: [...list.items, newItem] };
}
return list;
}));
// Prevent adding a duplicate master item.
if (item.masterItemId) {
const itemExists = targetList.items.some((i) => i.master_item_id === item.masterItemId);
if (itemExists) {
// Optionally, we could show a toast notification here.
console.log(
`useShoppingLists: Item with master ID ${item.masterItemId} already in list.`,
);
return; // Exit without calling the API.
}
}
} catch (e) {
console.error('useShoppingLists: Failed to add item.', e);
}
}, [userProfile, shoppingLists, setShoppingLists, addItemApi]);
const updateItemInList = useCallback(async (itemId: number, updates: Partial<ShoppingListItem>) => {
if (!userProfile || !activeListId) return;
try {
const updatedItem = await updateItemApi(itemId, updates);
if (updatedItem) {
setShoppingLists(prevLists => prevLists.map(list => {
if (list.shopping_list_id === activeListId) {
return { ...list, items: list.items.map(i => i.shopping_list_item_id === itemId ? updatedItem : i) };
}
return list;
}));
try {
const newItem = await addItemApi(listId, item);
if (newItem) {
setShoppingLists((prevLists) =>
prevLists.map((list) => {
if (list.shopping_list_id === listId) {
// The duplicate check is now handled above, so we can just add the item.
return { ...list, items: [...list.items, newItem] };
}
return list;
}),
);
}
} catch (e) {
console.error('useShoppingLists: Failed to add item.', e);
}
} catch (e) {
console.error('useShoppingLists: Failed to update item.', e);
}
}, [userProfile, activeListId, setShoppingLists, updateItemApi]);
},
[userProfile, shoppingLists, setShoppingLists, addItemApi],
);
const removeItemFromList = useCallback(async (itemId: number) => {
if (!userProfile || !activeListId) return;
try {
const result = await removeItemApi(itemId);
if (result === null) {
setShoppingLists(prevLists => prevLists.map(list => {
if (list.shopping_list_id === activeListId) {
return { ...list, items: list.items.filter(i => i.shopping_list_item_id !== itemId) };
}
return list;
}));
const updateItemInList = useCallback(
async (itemId: number, updates: Partial<ShoppingListItem>) => {
if (!userProfile || !activeListId) return;
try {
const updatedItem = await updateItemApi(itemId, updates);
if (updatedItem) {
setShoppingLists((prevLists) =>
prevLists.map((list) => {
if (list.shopping_list_id === activeListId) {
return {
...list,
items: list.items.map((i) =>
i.shopping_list_item_id === itemId ? updatedItem : i,
),
};
}
return list;
}),
);
}
} catch (e) {
console.error('useShoppingLists: Failed to update item.', e);
}
} catch (e) {
console.error('useShoppingLists: Failed to remove item.', e);
}
}, [userProfile, activeListId, setShoppingLists, removeItemApi]);
},
[userProfile, activeListId, setShoppingLists, updateItemApi],
);
const removeItemFromList = useCallback(
async (itemId: number) => {
if (!userProfile || !activeListId) return;
try {
const result = await removeItemApi(itemId);
if (result === null) {
setShoppingLists((prevLists) =>
prevLists.map((list) => {
if (list.shopping_list_id === activeListId) {
return {
...list,
items: list.items.filter((i) => i.shopping_list_item_id !== itemId),
};
}
return list;
}),
);
}
} catch (e) {
console.error('useShoppingLists: Failed to remove item.', e);
}
},
[userProfile, activeListId, setShoppingLists, removeItemApi],
);
return {
shoppingLists,
@@ -173,4 +218,4 @@ const useShoppingListsHook = () => {
};
};
export { useShoppingListsHook as useShoppingLists };
export { useShoppingListsHook as useShoppingLists };