129 lines
3.7 KiB
TypeScript
129 lines
3.7 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo, useState } from "react";
|
|
|
|
type FilterValues = {
|
|
priceMin: string;
|
|
priceMax: string;
|
|
brand: string;
|
|
stockOnly: boolean;
|
|
sort: string;
|
|
};
|
|
|
|
const DEFAULTS: FilterValues = {
|
|
priceMin: "",
|
|
priceMax: "",
|
|
brand: "",
|
|
stockOnly: false,
|
|
sort: "popular",
|
|
};
|
|
|
|
export default function FilterCard({
|
|
title = "Filter",
|
|
onApply,
|
|
}: {
|
|
title?: string;
|
|
onApply?: (filters: FilterValues) => void;
|
|
}) {
|
|
const [values, setValues] = useState<FilterValues>(DEFAULTS);
|
|
const [applied, setApplied] = useState<FilterValues | null>(null);
|
|
|
|
const summary = useMemo(() => {
|
|
if (!applied) return "No filters applied.";
|
|
const parts: string[] = [];
|
|
if (applied.priceMin) parts.push(`Min $${applied.priceMin}`);
|
|
if (applied.priceMax) parts.push(`Max $${applied.priceMax}`);
|
|
if (applied.brand) parts.push(applied.brand);
|
|
if (applied.stockOnly) parts.push("In stock");
|
|
if (applied.sort) parts.push(`Sort: ${applied.sort}`);
|
|
return parts.length ? parts.join(" • ") : "No filters applied.";
|
|
}, [applied]);
|
|
|
|
function apply() {
|
|
setApplied(values);
|
|
onApply?.(values);
|
|
}
|
|
|
|
function reset() {
|
|
setValues(DEFAULTS);
|
|
setApplied(null);
|
|
onApply?.(DEFAULTS);
|
|
}
|
|
|
|
return (
|
|
<div className="card filter-card">
|
|
<div className="filter-card__title">{title}</div>
|
|
|
|
<div className="filter-card__group">
|
|
<label className="filter-card__label">Price</label>
|
|
<div className="filter-card__row">
|
|
<input
|
|
className="filter-card__input"
|
|
placeholder="Min"
|
|
value={values.priceMin}
|
|
onChange={(e) => setValues({ ...values, priceMin: e.target.value })}
|
|
inputMode="numeric"
|
|
/>
|
|
<input
|
|
className="filter-card__input"
|
|
placeholder="Max"
|
|
value={values.priceMax}
|
|
onChange={(e) => setValues({ ...values, priceMax: e.target.value })}
|
|
inputMode="numeric"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="filter-card__group">
|
|
<label className="filter-card__label">Brand</label>
|
|
<select
|
|
className="filter-card__select"
|
|
value={values.brand}
|
|
onChange={(e) => setValues({ ...values, brand: e.target.value })}
|
|
>
|
|
<option value="">All brands</option>
|
|
<option value="TrailForge">TrailForge</option>
|
|
<option value="RidgeLine">RidgeLine</option>
|
|
<option value="SummitWorks">SummitWorks</option>
|
|
<option value="IronPeak">IronPeak</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div className="filter-card__group">
|
|
<label className="filter-card__label">Sort</label>
|
|
<select
|
|
className="filter-card__select"
|
|
value={values.sort}
|
|
onChange={(e) => setValues({ ...values, sort: e.target.value })}
|
|
>
|
|
<option value="popular">Most popular</option>
|
|
<option value="new">Newest</option>
|
|
<option value="priceLow">Price: Low to High</option>
|
|
<option value="priceHigh">Price: High to Low</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div className="filter-card__toggle">
|
|
<input
|
|
id="stockOnly"
|
|
type="checkbox"
|
|
checked={values.stockOnly}
|
|
onChange={(e) => setValues({ ...values, stockOnly: e.target.checked })}
|
|
/>
|
|
<label htmlFor="stockOnly">In-stock only</label>
|
|
</div>
|
|
|
|
<div className="filter-card__actions">
|
|
<button className="btn" type="button" onClick={apply}>
|
|
Apply
|
|
</button>
|
|
<button className="btn btn--ghost" type="button" onClick={reset}>
|
|
Reset
|
|
</button>
|
|
</div>
|
|
|
|
<div className="filter-card__summary">{summary}</div>
|
|
</div>
|
|
);
|
|
}
|