Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 1x 14x 14x 14x 13x 13x 13x 37x 37x 37x 37x 14x 14x 14x 14x 14x 14x 14x 23x 11x 13x 11x 12x 11x 8x 8x 8x 14x 14x | // src/hooks/useActiveDeals.tsx
import { useMemo } from 'react';
import { useFlyers } from './useFlyers';
import { useUserData } from '../hooks/useUserData';
import { useFlyerItemsForFlyersQuery } from './queries/useFlyerItemsForFlyersQuery';
import { useFlyerItemCountQuery } from './queries/useFlyerItemCountQuery';
import type { DealItem } from '../types';
import { logger } from '../services/logger.client';
/**
* A custom hook to calculate currently active deals and total active items
* based on flyer validity dates and a user's watched items.
*
* Refactored to use TanStack Query (ADR-0005 Phase 6).
*
* @returns An object containing active deals, total active items, loading state, and any errors.
*/
export const useActiveDeals = () => {
const { flyers } = useFlyers();
const { watchedItems } = useUserData();
// Memoize the calculation of valid flyers to avoid re-computing on every render.
const validFlyers = useMemo(() => {
const today = new Date();
today.setHours(0, 0, 0, 0);
return flyers.filter((flyer) => {
const from = new Date(`${flyer.valid_from}T00:00:00`);
const to = new Date(`${flyer.valid_to}T00:00:00`);
const isValid = from <= today && today <= to;
return isValid;
});
}, [flyers]);
// Memoize valid flyer IDs for stable query keys
const validFlyerIds = useMemo(() => validFlyers.map((f) => f.flyer_id), [validFlyers]);
// Use TanStack Query for data fetching
const {
data: itemsData,
isLoading: loadingItems,
error: itemsError,
} = useFlyerItemsForFlyersQuery(
validFlyerIds,
validFlyerIds.length > 0 && watchedItems.length > 0,
);
const {
data: countData,
isLoading: loadingCount,
error: countError,
} = useFlyerItemCountQuery(validFlyerIds, validFlyerIds.length > 0);
// Consolidate loading and error states from both queries.
const isLoading = loadingCount || loadingItems;
const error =
itemsError || countError
? `Could not fetch active deals or totals: ${itemsError?.message || countError?.message}`
: null;
// Process the data returned from the query hooks.
const activeDeals = useMemo(() => {
if (!itemsData || watchedItems.length === 0) return [];
const watchedItemIds = new Set(watchedItems.map((item) => item.master_grocery_item_id));
const dealItemsRaw = itemsData.filter(
(item) => item.master_item_id && watchedItemIds.has(item.master_item_id),
);
const flyerIdToStoreName = new Map(
validFlyers.map((f) => [f.flyer_id, f.store?.name || 'Unknown Store']),
);
return dealItemsRaw.map((item): DealItem => {
const deal: DealItem = {
item: item.item,
price_display: item.price_display,
price_in_cents: item.price_in_cents ?? null,
quantity: item.quantity ?? '',
storeName: flyerIdToStoreName.get(item.flyer_id!) || 'Unknown Store',
master_item_name: item.master_item_name,
unit_price: item.unit_price ?? null,
};
// Logging the mapped deal for debugging purposes to trace data integrity issues
logger.info('Mapped DealItem:', deal);
return deal;
});
}, [itemsData, watchedItems, validFlyers]);
const totalActiveItems = countData?.count ?? 0;
return { activeDeals, totalActiveItems, isLoading, error };
};
|