function Shop({ go, initialCat }) {
const { PRODUCTS, CATEGORIES } = window.FURN;
const [cat, setCat] = useState(initialCat || "all");
const [sort, setSort] = useState("featured");
const [openFilter, setOpenFilter] = useState(false);
useEffect(() => { setCat(initialCat || "all"); }, [initialCat]);
useReveal();
const prices = PRODUCTS.map((p) => p.price);
const minLimit = prices.length > 0 ? Math.min(...prices) : 0;
const maxLimit = prices.length > 0 ? Math.max(...prices) : 10000;
const [minPrice, setMinPrice] = useState(minLimit);
const [maxPrice, setMaxPrice] = useState(maxLimit);
const [inStock, setInStock] = useState(false);
const [onSale, setOnSale] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
// Sync price limits if PRODUCTS changes
useEffect(() => {
setMinPrice(minLimit);
setMaxPrice(maxLimit);
}, [minLimit, maxLimit]);
const childSlugs = CATEGORIES.filter((c) => c.parent === cat).map((c) => c.id);
let list = PRODUCTS.filter((p) => {
const matchesCat = cat === "all" || p.cat === cat || childSlugs.includes(p.cat);
const matchesPrice = p.price >= minPrice && p.price <= maxPrice;
const matchesStock = !inStock || p.in_stock;
const matchesSale = !onSale || (p.old !== null && p.old > p.price);
const matchesSearch = !searchQuery.trim() ||
p.en.toLowerCase().includes(searchQuery.toLowerCase()) ||
p.ar.includes(searchQuery);
return matchesCat && matchesPrice && matchesStock && matchesSale && matchesSearch;
});
if (sort === "low") list = [...list].sort((a, b) => a.price - b.price);
else if (sort === "high") list = [...list].sort((a, b) => b.price - a.price);
else if (sort === "new") list = [...list].sort((a, b) => (b.badge?.en === "New") - (a.badge?.en === "New"));
const sorts = [
{ id: "featured", label: "Featured" },
{ id: "low", label: "Price ↑" },
{ id: "high", label: "Price ↓" },
{ id: "new", label: "Newest" },
];
const parents = CATEGORIES.filter((c) => !c.parent);
const getCatCount = (catId) => {
const childIds = CATEGORIES.filter((c) => c.parent === catId).map((c) => c.id);
return PRODUCTS.filter((p) => p.cat === catId || childIds.includes(p.cat)).length;
};
const getSubCatCount = (subId) => PRODUCTS.filter((p) => p.cat === subId).length;
const activeParent = cat !== "all"
? (CATEGORIES.find((c) => c.id === cat && !c.parent) || CATEGORIES.find((c) => c.id === (CATEGORIES.find((x) => x.id === cat)?.parent)))
: null;
const FilterPanel = () => (
{/* Search Filter */}
Search Collection
setSearchQuery(e.target.value)}
style={{ width: "100%", padding: "10px 12px", paddingRight: 36, background: "var(--surface)", border: "1px solid var(--line)", color: "var(--ink)", borderRadius: 4, fontSize: 14 }}
/>
{searchQuery && (
)}
{/* Category Hierarchy */}
Category
{activeParent ? (
<>
{/* Back to all button */}
-
{/* Active Parent category item */}
-
{/* Subcategories under this parent */}
{CATEGORIES.filter((sub) => sub.parent === activeParent.id).map((sub) => {
const subCount = getSubCatCount(sub.id);
if (subCount === 0) return null;
return (
-
);
})}
>
) : (
<>
{/* All pieces button when no parent is selected */}
-
{/* List of all parent categories */}
{parents.map((c) => {
const count = getCatCount(c.id);
if (count === 0) return null;
return (
-
);
})}
>
)}
{/* Dynamic Price Range */}
Price Range
setMinPrice(Math.max(minLimit, Math.min(maxPrice, +e.target.value)))}
style={{ width: "100%", padding: "6px 8px", background: "var(--surface)", border: "1px solid var(--line)", color: "var(--ink)", borderRadius: 3, fontSize: 13 }}
/>
setMaxPrice(Math.min(maxLimit, Math.max(minPrice, +e.target.value)))}
style={{ width: "100%", padding: "6px 8px", background: "var(--surface)", border: "1px solid var(--line)", color: "var(--ink)", borderRadius: 3, fontSize: 13 }}
/>
setMaxPrice(+e.target.value)}
className="range"
style={{ width: "100%" }}
/>
{money(minLimit)}
{money(maxLimit)}
{/* Availability & Sale */}
{/* Sort Options */}
Sort
{sorts.map((s) => (
))}
{/* Reset Filter Button */}
{(cat !== "all" || minPrice !== minLimit || maxPrice !== maxLimit || inStock || onSale || searchQuery) && (
)}
);
return (
{/* header band */}
The full collection
{cat === "all" ? "Every piece" : (CATEGORIES.find((c) => c.id === cat)?.en || "Collection")}
{/* sidebar */}
{/* grid */}
{list.length} {list.length === 1 ? "piece" : "pieces"}
{list.length === 0 ? (
No pieces match this filter.
) : (
)}
{/* mobile filter drawer */}
setOpenFilter(false)} style={{ position: "fixed", inset: 0, zIndex: 70, background: "oklch(0 0 0 /.5)", opacity: openFilter ? 1 : 0, pointerEvents: openFilter ? "auto" : "none", transition: "opacity .35s" }} />
);
}
Object.assign(window, { Shop });