// src/category.jsx — Category deep-dive: filter + sort + bigger grid for a single aisle. function Category({ storeId, category, onNav, cart, addToCart, tweaks }) { const isMobile = useMedia("(max-width: 720px)"); const store = STORES.find(s => s.id === storeId) || STORES[0]; const cat = CATEGORIES.find(c => c.id === category) || CATEGORIES[1]; const [sort, setSort] = React.useState("popular"); const [sub, setSub] = React.useState("all"); const all = PRODUCTS.filter(p => p.category === cat.id); const subs = ["all", ...Array.from(new Set(all.map(p => p.subcategory).filter(Boolean)))]; let list = sub === "all" ? all : all.filter(p => p.subcategory === sub); if (sort === "priceAsc") list = [...list].sort((a,b) => a.price - b.price); if (sort === "priceDesc") list = [...list].sort((a,b) => b.price - a.price); if (sort === "popular") list = [...list].sort((a,b) => (b.popular?1:0) - (a.popular?1:0)); const cols = isMobile ? 2 : tweaks.density === "compact" ? 6 : tweaks.density === "comfy" ? 4 : 5; const getQty = (pid) => cart.items.find(i => i.productId === pid)?.qty || 0; const Icon = Icons[cat.icon]; return (
{/* breadcrumb */}
{cat.label}
{all.length} items

{cat.label}

{[ { id: "popular", label: "Popular" }, { id: "priceAsc", label: "Price ↑" }, { id: "priceDesc", label: "Price ↓" }, ].map(s => ( ))}
{/* Subcategory chips */} {subs.length > 2 && (
{subs.map(s => ( ))}
)} {list.length === 0 ? ( setSub("all")} subActive={sub} /> ) : (
{list.map(p => ( onNav({ screen: "product", storeId, productId: p.id })} onAdd={() => p.customizable ? onNav({ screen: "product", storeId, productId: p.id }) : addToCart(storeId, p.id, 1)} onRemove={() => addToCart(storeId, p.id, -1)} pattern={tweaks.addPattern} density={tweaks.density} /> ))}
)}
); } // Empty-state for a category aisle — either the store doesn't stock this // category or the active sub-filter is too narrow. Offers three ways out: // clear the filter, Ask Opa, or jump to a nearby store that does stock it. function CategoryEmpty({ cat, store, onNav, onClearFilter, subActive }) { const alts = STORES.filter(s => s.id !== store.id).slice(0, 3); const Icon = Icons[cat.icon]; const filtered = subActive !== "all"; return (

{filtered ? `Nothing matches "${subActive}" here.` : `${store.name} doesn't stock ${cat.label} today.`}

{filtered ? "Clear the filter to see everything in this aisle, or ask Opa to find it across stores." : `Three stores nearby carry ${cat.label.toLowerCase()}. One of them is probably 6 minutes closer than you think.`}

{filtered && ( Clear filter )} } onClick={() => onNav({ screen: "home" })}> Ask Opa
{!filtered && alts.length > 0 && (
Try nearby {alts.map(s => ( ))}
)}
); } window.Category = Category;