All files / src/hooks useActiveDeals.tsx

100% Statements 33/33
96.66% Branches 29/30
100% Functions 10/10
100% Lines 29/29

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 };
};