no longer price sort of frontend

This commit is contained in:
2026-01-02 09:56:51 -05:00
parent 228d08337f
commit b7e3db8d34
2 changed files with 49 additions and 25 deletions
+39 -17
View File
@@ -140,43 +140,58 @@ export default function PartsBrowseClient(props: {
// ---------------------------- // ----------------------------
useEffect(() => { useEffect(() => {
if (!partRole) return; if (!partRole) return;
const controller = new AbortController(); const controller = new AbortController();
async function fetchParts() { async function fetchParts() {
try { try {
setLoading(true); setLoading(true);
setError(null); setError(null);
const search = new URLSearchParams(); const search = new URLSearchParams();
search.set("platform", effectivePlatform); search.set("platform", effectivePlatform);
search.append("partRole", partRole); search.append("partRole", partRole);
// server paging (0-based) // server paging (0-based)
search.set("page", String(currentPage - 1)); search.set("page", String(currentPage - 1));
search.set("size", String(PAGE_SIZE)); search.set("size", String(PAGE_SIZE));
// optional server search + sort (backend can ignore for now) // optional server search + sort (backend can ignore for now)
if (searchQuery.trim()) search.set("q", searchQuery.trim()); if (searchQuery.trim()) search.set("q", searchQuery.trim());
if (brandFilter.length) brandFilter.forEach((b) => search.append("brand", b)); if (brandFilter.length)
brandFilter.forEach((b) => search.append("brand", b));
// sort mapping // sort mapping
// sort mapping (Spring-style: sort=field,dir)
switch (sortBy) { switch (sortBy) {
case "price-asc":
search.set("sort", "price,asc");
break;
case "price-desc":
search.set("sort", "price,desc");
break;
case "brand-asc":
search.set("sort", "brand,asc");
break;
case "relevance": case "relevance":
default: default:
// fallback (or whatever you consider "relevance")
search.set("sort", "updatedAt,desc"); search.set("sort", "updatedAt,desc");
break; break;
} }
const res = await fetch(`${API_BASE_URL}/api/v1/catalog/options?${search.toString()}`, { const res = await fetch(
signal: controller.signal, `${API_BASE_URL}/api/v1/catalog/options?${search.toString()}`,
}); {
signal: controller.signal,
}
);
if (!res.ok) throw new Error(`Failed to load products (${res.status})`); if (!res.ok) throw new Error(`Failed to load products (${res.status})`);
const json = await res.json(); const json = await res.json();
const { items, page } = unwrapContent<GunbuilderProductFromApi>(json); const { items, page } = unwrapContent<GunbuilderProductFromApi>(json);
setParts( setParts(
items.map((p) => ({ items.map((p) => ({
id: normalizeId(p.id), id: normalizeId(p.id),
@@ -190,7 +205,7 @@ export default function PartsBrowseClient(props: {
inStock: p.inStock ?? true, inStock: p.inStock ?? true,
})) }))
); );
if (page) { if (page) {
setServerTotalPages(page.totalPages ?? 1); setServerTotalPages(page.totalPages ?? 1);
setServerTotalElements(page.totalElements ?? items.length); setServerTotalElements(page.totalElements ?? items.length);
@@ -206,10 +221,17 @@ export default function PartsBrowseClient(props: {
setLoading(false); setLoading(false);
} }
} }
fetchParts(); fetchParts();
return () => controller.abort(); return () => controller.abort();
}, [partRole, effectivePlatform, currentPage, searchQuery, sortBy, brandFilter]); }, [
partRole,
effectivePlatform,
currentPage,
searchQuery,
sortBy,
brandFilter,
]);
// Reset pagination on filters // Reset pagination on filters
useEffect(() => { useEffect(() => {
setCurrentPage(1); setCurrentPage(1);
+10 -8
View File
@@ -47,21 +47,23 @@ export function slugify(input: string) {
export async function fetchProducts(params: { export async function fetchProducts(params: {
platform: string; platform: string;
partRole: string; partRole: string;
sort?: string; // ex: "price,asc"
}) { }) {
const { platform, partRole } = params; const { platform, partRole, sort } = params;
const url = const sp = new URLSearchParams({
`${API_BASE_URL}/api/v1/products?` + platform,
new URLSearchParams({ partRoles: partRole,
platform, });
partRoles: partRole,
}); if (sort) sp.set("sort", sort);
const url = `${API_BASE_URL}/api/v1/products?${sp.toString()}`;
const res = await fetch(url, { cache: "no-store" }); const res = await fetch(url, { cache: "no-store" });
if (!res.ok) throw new Error(`Failed to load products (${res.status})`); if (!res.ok) throw new Error(`Failed to load products (${res.status})`);
const data = (await res.json()) as ProductListItem[]; const data = (await res.json()) as ProductListItem[];
// Ensure each item has a "productSlug" of the form "{id}-{slugified-name}"
return data.map((p) => ({ return data.map((p) => ({
...p, ...p,
productSlug: `${p.id}-${slugify(p.name)}`, productSlug: `${p.id}-${slugify(p.name)}`,