From 5879328b6712cc2d7920ee5883d01481b3c004e9 Mon Sep 17 00:00:00 2001 From: Torben Sorensen Date: Mon, 19 Jan 2026 19:10:02 -0800 Subject: [PATCH] fixing categories 3rd normal form --- server.ts | 3 + .../flyer/ExtractedDataTable.test.tsx | 7 +- src/features/flyer/ExtractedDataTable.tsx | 10 +- .../shopping/WatchedItemsList.test.tsx | 81 ++++---- src/features/shopping/WatchedItemsList.tsx | 26 +-- .../useAddWatchedItemMutation.test.tsx | 24 +-- .../mutations/useAddWatchedItemMutation.ts | 8 +- src/hooks/useWatchedItems.test.tsx | 8 +- src/hooks/useWatchedItems.tsx | 4 +- src/routes/category.routes.ts | 195 ++++++++++++++++++ src/routes/user.routes.test.ts | 12 +- src/routes/user.routes.ts | 4 +- src/services/apiClient.test.ts | 8 +- src/services/apiClient.ts | 4 +- src/services/db/category.db.ts | 92 +++++++++ src/services/db/personalization.db.test.ts | 30 +-- src/services/db/personalization.db.ts | 17 +- src/tests/e2e/deals-journey.e2e.test.ts | 71 ++++++- src/tests/integration/category.routes.test.ts | 174 ++++++++++++++++ .../integration/user.integration.test.ts | 9 +- 20 files changed, 655 insertions(+), 132 deletions(-) create mode 100644 src/routes/category.routes.ts create mode 100644 src/services/db/category.db.ts create mode 100644 src/tests/integration/category.routes.test.ts diff --git a/server.ts b/server.ts index 7120897..f7623df 100644 --- a/server.ts +++ b/server.ts @@ -38,6 +38,7 @@ import receiptRouter from './src/routes/receipt.routes'; import dealsRouter from './src/routes/deals.routes'; import reactionsRouter from './src/routes/reactions.routes'; import storeRouter from './src/routes/store.routes'; +import categoryRouter from './src/routes/category.routes'; import { errorHandler } from './src/middleware/errorHandler'; import { backgroundJobService, startBackgroundJobs } from './src/services/backgroundJobService'; import { websocketService } from './src/services/websocketService.server'; @@ -288,6 +289,8 @@ app.use('/api/deals', dealsRouter); app.use('/api/reactions', reactionsRouter); // 16. Store management routes. app.use('/api/stores', storeRouter); +// 17. Category discovery routes (ADR-023: Database Normalization) +app.use('/api/categories', categoryRouter); // --- Error Handling and Server Startup --- diff --git a/src/features/flyer/ExtractedDataTable.test.tsx b/src/features/flyer/ExtractedDataTable.test.tsx index dab9809..d4328e4 100644 --- a/src/features/flyer/ExtractedDataTable.test.tsx +++ b/src/features/flyer/ExtractedDataTable.test.tsx @@ -58,6 +58,7 @@ const mockFlyerItems: FlyerItem[] = [ quantity: 'per lb', unit_price: { value: 1.99, unit: 'lb' }, master_item_id: 1, + category_id: 1, category_name: 'Produce', flyer_id: 1, }), @@ -69,6 +70,7 @@ const mockFlyerItems: FlyerItem[] = [ quantity: '4L', unit_price: { value: 1.125, unit: 'L' }, master_item_id: 2, + category_id: 2, category_name: 'Dairy', flyer_id: 1, }), @@ -80,6 +82,7 @@ const mockFlyerItems: FlyerItem[] = [ quantity: 'per kg', unit_price: { value: 8.0, unit: 'kg' }, master_item_id: 3, + category_id: 3, category_name: 'Meat', flyer_id: 1, }), @@ -241,7 +244,7 @@ describe('ExtractedDataTable', () => { expect(watchButton).toBeInTheDocument(); fireEvent.click(watchButton); - expect(mockAddWatchedItem).toHaveBeenCalledWith('Chicken Breast', 'Meat'); + expect(mockAddWatchedItem).toHaveBeenCalledWith('Chicken Breast', 3); }); it('should not show watch or add to list buttons for unmatched items', () => { @@ -589,7 +592,7 @@ describe('ExtractedDataTable', () => { const watchButton = within(itemRow).getByTitle("Add 'Canonical Mystery' to your watchlist"); fireEvent.click(watchButton); - expect(mockAddWatchedItem).toHaveBeenCalledWith('Canonical Mystery', 'Other/Miscellaneous'); + expect(mockAddWatchedItem).toHaveBeenCalledWith('Canonical Mystery', 19); }); it('should not call addItemToList when activeListId is null and button is clicked', () => { diff --git a/src/features/flyer/ExtractedDataTable.tsx b/src/features/flyer/ExtractedDataTable.tsx index 80c62c9..112997d 100644 --- a/src/features/flyer/ExtractedDataTable.tsx +++ b/src/features/flyer/ExtractedDataTable.tsx @@ -25,7 +25,7 @@ interface ExtractedDataTableRowProps { isAuthenticated: boolean; activeListId: number | null; onAddItemToList: (masterItemId: number) => void; - onAddWatchedItem: (itemName: string, category: string) => void; + onAddWatchedItem: (itemName: string, category_id: number) => void; } /** @@ -72,9 +72,7 @@ const ExtractedDataTableRow: React.FC = memo( )} {isAuthenticated && !isWatched && canonicalName && (