import { useState, useEffect } from 'react' import { useQuery } from '@tanstack/react-query' import { Printer, ShoppingCart, Package, Tag, Receipt, RotateCcw } from 'lucide-react' import { mealPlannerApi } from '../api' import type { ShoppingList } from '../types' import { Button } from '../components/ui/Button' import { Badge } from '../components/ui/Badge' import { Card, CardBody } from '../components/ui/Card' import { Skeleton, SkeletonText } from '../components/ui/Skeleton' import { EmptyState } from '../components/ui/EmptyState' const AISLE_LABEL: Record = { produce: 'Produce', meat: 'Meat & Seafood', seafood: 'Meat & Seafood', meat_seafood: 'Meat & Seafood', chicken: 'Meat & Seafood', beef: 'Meat & Seafood', pork: 'Meat & Seafood', dairy: 'Dairy & Eggs', eggs: 'Dairy & Eggs', cheese: 'Dairy & Eggs', milk: 'Dairy & Eggs', yogurt: 'Dairy & Eggs', pantry: 'Pantry', canned: 'Pantry', canned_goods: 'Pantry', dry: 'Pantry', snacks: 'Pantry', frozen: 'Frozen', bakery: 'Bakery', bread: 'Bakery', beverages: 'Beverages', drinks: 'Beverages', spices: 'Spices', seasoning: 'Spices', other: 'Other', } function aisleDisplay(key: string): string { return AISLE_LABEL[key.toLowerCase()] ?? key } function ShoppingListSkeleton() { return (
) } function SkeletonCard() { return ( ) } function storageKey(week: string) { return `shopping-list-checked-${week}` } export default function ShoppingListPage() { const { data: shoppingList, isLoading } = useQuery({ queryKey: ['shoppingList'], queryFn: () => mealPlannerApi.shoppingList.get().then(r => r.data), }) const week = shoppingList?.week_start_date || '' const [checked, setChecked] = useState>(new Set()) useEffect(() => { if (!week) return try { const raw = localStorage.getItem(storageKey(week)) if (raw) { const parsed = JSON.parse(raw) as string[] setChecked(new Set(parsed)) } else { setChecked(new Set()) } } catch { setChecked(new Set()) } }, [week]) useEffect(() => { if (!week) return localStorage.setItem(storageKey(week), JSON.stringify(Array.from(checked))) }, [checked, week]) const toggle = (id: string) => { setChecked(prev => { const next = new Set(prev) if (next.has(id)) next.delete(id) else next.add(id) return next }) } const clearAll = () => setChecked(new Set()) const progress = shoppingList && shoppingList.items.length > 0 ? Math.round( (Array.from(checked).filter(id => shoppingList.items.some(i => i.ingredient_id === id) ).length / shoppingList.items.length) * 100 ) : 0 if (isLoading) { return } if (!shoppingList || shoppingList.items.length === 0) { return (

Shopping List

) } return (
{/* Header */}

Shopping List

Week of {new Date(shoppingList.week_start_date).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}

{progress > 0 && ( {progress}% complete )} {checked.size > 0 && ( )}
{/* Summary Stats */}
${shoppingList.total_estimated_cost.toFixed(2)}
Estimated
{shoppingList.items.length}
Items
{shoppingList.sale_items_count}
On Sale
{/* Aisle Groups */}
{Object.entries(shoppingList.by_aisle).map(([aisle, items]) => (

{aisleDisplay(aisle)}

{items.length} items
    {items.map((item, idx) => { const isChecked = item.ingredient_id ? checked.has(item.ingredient_id) : false return (
  • item.ingredient_id && toggle(item.ingredient_id)} />
    {item.name}
    {item.in_pantry && In Pantry} {item.is_on_sale && SALE}
    {item.quantity && ( {item.quantity} {item.unit || ''} )} {item.sale_price ? (
    ${item.sale_price.toFixed(2)} {item.estimated_price && item.estimated_price > item.sale_price && ( ${item.estimated_price.toFixed(2)} )}
    ) : item.estimated_price ? ( ${item.estimated_price.toFixed(2)} ) : null}
  • ) })}
))}
) }