From b7e3db8d34e3a66d843f293e026003b05866423e Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 2 Jan 2026 09:56:51 -0500 Subject: [PATCH] no longer price sort of frontend --- components/parts/PartsBrowseClient.tsx | 56 ++++++++++++++++++-------- lib/catalog.ts | 18 +++++---- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/components/parts/PartsBrowseClient.tsx b/components/parts/PartsBrowseClient.tsx index 33a5a60..8cd02b9 100644 --- a/components/parts/PartsBrowseClient.tsx +++ b/components/parts/PartsBrowseClient.tsx @@ -140,43 +140,58 @@ export default function PartsBrowseClient(props: { // ---------------------------- useEffect(() => { if (!partRole) return; - + const controller = new AbortController(); - + async function fetchParts() { try { setLoading(true); setError(null); - + const search = new URLSearchParams(); search.set("platform", effectivePlatform); search.append("partRole", partRole); - + // server paging (0-based) search.set("page", String(currentPage - 1)); search.set("size", String(PAGE_SIZE)); - + // optional server search + sort (backend can ignore for now) 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 (Spring-style: sort=field,dir) 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": default: + // fallback (or whatever you consider "relevance") search.set("sort", "updatedAt,desc"); break; } - - const res = await fetch(`${API_BASE_URL}/api/v1/catalog/options?${search.toString()}`, { - signal: controller.signal, - }); - + + const res = await fetch( + `${API_BASE_URL}/api/v1/catalog/options?${search.toString()}`, + { + signal: controller.signal, + } + ); + if (!res.ok) throw new Error(`Failed to load products (${res.status})`); - + const json = await res.json(); const { items, page } = unwrapContent(json); - + setParts( items.map((p) => ({ id: normalizeId(p.id), @@ -190,7 +205,7 @@ export default function PartsBrowseClient(props: { inStock: p.inStock ?? true, })) ); - + if (page) { setServerTotalPages(page.totalPages ?? 1); setServerTotalElements(page.totalElements ?? items.length); @@ -206,10 +221,17 @@ export default function PartsBrowseClient(props: { setLoading(false); } } - + fetchParts(); return () => controller.abort(); - }, [partRole, effectivePlatform, currentPage, searchQuery, sortBy, brandFilter]); + }, [ + partRole, + effectivePlatform, + currentPage, + searchQuery, + sortBy, + brandFilter, + ]); // Reset pagination on filters useEffect(() => { setCurrentPage(1); diff --git a/lib/catalog.ts b/lib/catalog.ts index 8c41b7b..1944101 100644 --- a/lib/catalog.ts +++ b/lib/catalog.ts @@ -47,21 +47,23 @@ export function slugify(input: string) { export async function fetchProducts(params: { platform: string; partRole: string; + sort?: string; // ex: "price,asc" }) { - const { platform, partRole } = params; + const { platform, partRole, sort } = params; - const url = - `${API_BASE_URL}/api/v1/products?` + - new URLSearchParams({ - platform, - partRoles: partRole, - }); + const sp = new URLSearchParams({ + 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" }); if (!res.ok) throw new Error(`Failed to load products (${res.status})`); const data = (await res.json()) as ProductListItem[]; - // Ensure each item has a "productSlug" of the form "{id}-{slugified-name}" return data.map((p) => ({ ...p, productSlug: `${p.id}-${slugify(p.name)}`,