no longer price sort of frontend
This commit is contained in:
@@ -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
@@ -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)}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user