support for filtering and updating caliber
This commit is contained in:
@@ -8,7 +8,9 @@ export default function Filters(props: {
|
||||
availableBrands: string[];
|
||||
brandFilter: string[];
|
||||
setBrandFilter: (v: string[]) => void;
|
||||
|
||||
availableCalibers: string[];
|
||||
caliberFilter: string[];
|
||||
setCaliberFilter: (next: string[]) => void;
|
||||
priceBounds: { min: number | null; max: number | null };
|
||||
priceRange: { min: number | null; max: number | null };
|
||||
setPriceRange: (v: { min: number | null; max: number | null }) => void;
|
||||
@@ -20,6 +22,9 @@ export default function Filters(props: {
|
||||
availableBrands,
|
||||
brandFilter,
|
||||
setBrandFilter,
|
||||
availableCalibers,
|
||||
caliberFilter,
|
||||
setCaliberFilter,
|
||||
priceBounds,
|
||||
priceRange,
|
||||
setPriceRange,
|
||||
@@ -88,7 +93,10 @@ export default function Filters(props: {
|
||||
onChange={(e) => {
|
||||
const value = Number(e.target.value);
|
||||
setPriceRange({
|
||||
min: Math.min(value, priceRange.max ?? priceBounds.max ?? value),
|
||||
min: Math.min(
|
||||
value,
|
||||
priceRange.max ?? priceBounds.max ?? value
|
||||
),
|
||||
max: priceRange.max ?? priceBounds.max ?? value,
|
||||
});
|
||||
}}
|
||||
@@ -104,7 +112,10 @@ export default function Filters(props: {
|
||||
const value = Number(e.target.value);
|
||||
setPriceRange({
|
||||
min: priceRange.min ?? priceBounds.min ?? value,
|
||||
max: Math.max(value, priceRange.min ?? priceBounds.min ?? value),
|
||||
max: Math.max(
|
||||
value,
|
||||
priceRange.min ?? priceBounds.min ?? value
|
||||
),
|
||||
});
|
||||
}}
|
||||
className="w-full"
|
||||
@@ -168,6 +179,38 @@ export default function Filters(props: {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{availableCalibers?.length > 0 && (
|
||||
<div className="mt-4">
|
||||
<div className="mb-2 text-xs font-semibold tracking-wide text-zinc-300">
|
||||
Caliber
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
{availableCalibers.map((c) => {
|
||||
const checked = caliberFilter.includes(c);
|
||||
return (
|
||||
<label
|
||||
key={c}
|
||||
className="flex items-center gap-2 text-sm text-zinc-300"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked)
|
||||
setCaliberFilter([...caliberFilter, c]);
|
||||
else
|
||||
setCaliberFilter(caliberFilter.filter((x) => x !== c));
|
||||
}}
|
||||
/>
|
||||
<span>{c}</span>
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* In-stock toggle */}
|
||||
<div className="border-t border-zinc-800 pt-3">
|
||||
<label className="flex cursor-pointer items-center justify-between text-[11px] text-zinc-200">
|
||||
@@ -187,4 +230,4 @@ export default function Filters(props: {
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ export default function PartsBrowseClient(props: {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [brandFilter, setBrandFilter] = useState<string[]>([]);
|
||||
const [caliberFilter, setCaliberFilter] = useState<string[]>([]);
|
||||
const [sortBy, setSortBy] = useState<SortOption>("relevance");
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [priceRange, setPriceRange] = useState<{
|
||||
@@ -151,7 +152,10 @@ export default function PartsBrowseClient(props: {
|
||||
if (brandFilter.length)
|
||||
brandFilter.forEach((b) => search.append("brand", b));
|
||||
|
||||
// sort mapping
|
||||
// server search caliber
|
||||
if (caliberFilter.length)
|
||||
caliberFilter.forEach((c) => search.append("caliber", c));
|
||||
|
||||
// sort mapping (Spring-style: sort=field,dir)
|
||||
switch (sortBy) {
|
||||
case "price-asc":
|
||||
@@ -222,6 +226,7 @@ export default function PartsBrowseClient(props: {
|
||||
searchQuery,
|
||||
sortBy,
|
||||
brandFilter,
|
||||
caliberFilter,
|
||||
]);
|
||||
// Reset pagination on filters
|
||||
useEffect(() => {
|
||||
@@ -230,6 +235,7 @@ export default function PartsBrowseClient(props: {
|
||||
partRole,
|
||||
effectivePlatform,
|
||||
brandFilter,
|
||||
caliberFilter,
|
||||
sortBy,
|
||||
searchQuery,
|
||||
priceRange,
|
||||
@@ -252,6 +258,14 @@ export default function PartsBrowseClient(props: {
|
||||
[parts]
|
||||
);
|
||||
|
||||
const availableCalibers = useMemo(
|
||||
() =>
|
||||
Array.from(new Set(parts.map((p: any) => p.caliber)))
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => String(a).localeCompare(String(b))),
|
||||
[parts]
|
||||
);
|
||||
|
||||
const priceBounds = useMemo(() => {
|
||||
const prices = parts
|
||||
.map((p) => p.price)
|
||||
@@ -406,6 +420,9 @@ export default function PartsBrowseClient(props: {
|
||||
availableBrands={availableBrands}
|
||||
brandFilter={brandFilter}
|
||||
setBrandFilter={setBrandFilter}
|
||||
availableCalibers={availableCalibers}
|
||||
caliberFilter={caliberFilter}
|
||||
setCaliberFilter={setCaliberFilter}
|
||||
priceBounds={priceBounds}
|
||||
priceRange={priceRange}
|
||||
setPriceRange={setPriceRange}
|
||||
|
||||
Reference in New Issue
Block a user