/* global React, ReactDOM, I, CarMedia, Nav, Footer, FabWa, useReveal, INVENTORY, fmtPrice, fmtKm, fuelLabel, transmissionLabel, carStatus, carDrive, fullModel, fullVariant, useInventory */ const { useState, useMemo, useEffect, useRef } = React; /* INVENTORY now comes from shared.jsx */ /* ─── DROPDOWN FILTER ──────────────────────────────────────── */ function FilterDropdown({ label, count, children }) { const [open, setOpen] = useState(false); const ref = useRef(null); useEffect(() => { const onClick = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; document.addEventListener("mousedown", onClick); return () => document.removeEventListener("mousedown", onClick); }, []); return (
{open &&
{children}
}
); } /* ─── SORT DROPDOWN (custom single-select, themed) ─────────── */ function SortDropdown({ value, onChange, options }) { const [open, setOpen] = useState(false); const ref = useRef(null); useEffect(() => { const onClick = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; document.addEventListener("mousedown", onClick); return () => document.removeEventListener("mousedown", onClick); }, []); const current = options.find((o) => o.v === value) || options[0]; return (
{open && (
{options.map((o) => ( ))}
)}
); } /* ─── INDIVIDUAL FILTER PANELS ─────────────────────────────── */ function MultiSelect({ title, options, value, onChange, counts }) { const toggle = (v) => { onChange(value.includes(v) ? value.filter((x) => x !== v) : [...value, v]); }; return ( <>
{title}
{options.map((o) => ( ))}
); } function RangeFilter({ title, min, max, value, onChange, unit }) { const [lo, hi] = value; return ( <>
{title}
onChange([e.target.value === "" ? null : Number(e.target.value), hi])} />
onChange([lo, e.target.value === "" ? null : Number(e.target.value)])} />
); } /* ─── PAGE ─────────────────────────────────────────────────── */ const PAGE_SIZE = 9; function AanbodPage() { useReveal(); const { cars: liveCars, loading, fromLive } = useInventory(); const INV = liveCars; const [q, setQ] = useState(""); const [brands, setBrands] = useState([]); const [bodies, setBodies] = useState([]); const [fuels, setFuels] = useState([]); const [transmissions, setTrans] = useState([]); const [priceRange, setPriceRange] = useState([null, null]); const [yearRange, setYearRange] = useState([null, null]); const [sort, setSort] = useState("recent"); const [view, setView] = useState("grid"); const [page, setPage] = useState(1); // derived filter option lists + counts const allBrands = useMemo(() => [...new Set(INV.map(c => c.merk))].sort(), [INV]); const allBodies = useMemo(() => [...new Set(INV.map(c => c.carrosserie))].sort(), [INV]); const allFuels = useMemo(() => [...new Set(INV.map(c => c.brandstof))].sort(), [INV]); const allTrans = useMemo(() => [...new Set(INV.map(c => c.transmissie))].sort(), [INV]); const brandCounts = useMemo(() => Object.fromEntries(allBrands.map(b => [b, INV.filter(c => c.merk === b).length])), [allBrands, INV]); const filtered = useMemo(() => { let r = INV.filter((c) => { const hay = (c.merk + " " + c.model + " " + (c.type || "") + " " + (c.uitrustingsniveau || "")).toLowerCase(); if (q && !hay.includes(q.toLowerCase())) return false; if (brands.length && !brands.includes(c.merk)) return false; if (bodies.length && !bodies.includes(c.carrosserie)) return false; if (fuels.length && !fuels.includes(c.brandstof)) return false; if (transmissions.length && !transmissions.includes(c.transmissie)) return false; if (priceRange[0] != null && c.verkoopprijs_particulier < priceRange[0]) return false; if (priceRange[1] != null && c.verkoopprijs_particulier > priceRange[1]) return false; if (yearRange[0] != null && c.bouwjaar < yearRange[0]) return false; if (yearRange[1] != null && c.bouwjaar > yearRange[1]) return false; return true; }); if (sort === "price-asc") r.sort((a, b) => a.verkoopprijs_particulier - b.verkoopprijs_particulier); else if (sort === "price-desc") r.sort((a, b) => b.verkoopprijs_particulier - a.verkoopprijs_particulier); else if (sort === "year-desc") r.sort((a, b) => b.bouwjaar - a.bouwjaar); else if (sort === "km-asc") r.sort((a, b) => a.tellerstand - b.tellerstand); // 'recent' = source order return r; }, [INV, q, brands, bodies, fuels, transmissions, priceRange, yearRange, sort]); // reset page when filters change useEffect(() => { setPage(1); }, [q, brands, bodies, fuels, transmissions, priceRange, yearRange, sort]); const pageCount = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE)); const pageItems = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE); // active filter chips const chips = []; brands.forEach((b) => chips.push({ k: "brand-" + b, label: b, onRm: () => setBrands(brands.filter(x => x !== b)) })); bodies.forEach((b) => chips.push({ k: "body-" + b, label: b, onRm: () => setBodies(bodies.filter(x => x !== b)) })); fuels.forEach((f) => chips.push({ k: "fuel-" + f, label: f, onRm: () => setFuels(fuels.filter(x => x !== f)) })); transmissions.forEach((t) => chips.push({ k: "trans-" + t, label: t, onRm: () => setTrans(transmissions.filter(x => x !== t)) })); if (priceRange[0] != null || priceRange[1] != null) { chips.push({ k: "price", label: "Prijs " + (priceRange[0] != null ? "€" + priceRange[0] : "") + "–" + (priceRange[1] != null ? "€" + priceRange[1] : ""), onRm: () => setPriceRange([null, null]) }); } if (yearRange[0] != null || yearRange[1] != null) { chips.push({ k: "year", label: "Jaar " + (yearRange[0] != null ? yearRange[0] : "") + "–" + (yearRange[1] != null ? yearRange[1] : ""), onRm: () => setYearRange([null, null]) }); } if (q) chips.push({ k: "q", label: "\u201C" + q + "\u201D", onRm: () => setQ("") }); const clearAll = () => { setQ(""); setBrands([]); setBodies([]); setFuels([]); setTrans([]); setPriceRange([null, null]); setYearRange([null, null]); }; const activeCount = chips.length; // pagination renderer const renderPagi = () => { if (pageCount <= 1) return null; const items = []; items.push(); for (let p = 1; p <= pageCount; p++) { if (p === 1 || p === pageCount || Math.abs(p - page) <= 1) { items.push(); } else if (p === 2 || p === pageCount - 1) { items.push(); } } items.push(); return
{items}
; }; return ( <>