53 lines
1.9 KiB
TypeScript
53 lines
1.9 KiB
TypeScript
import type { ProductListQuery } from '@sport/api-client';
|
|
|
|
/**
|
|
* Parses listing filters out of a Next.js `searchParams` object.
|
|
*
|
|
* Kept deliberately permissive: unknown or malformed values are dropped rather
|
|
* than rejected, because a listing page reached from a stale bookmark should
|
|
* render products, not an error. The API re-validates everything anyway
|
|
* (@sport/validation), so this is convenience, not enforcement.
|
|
*/
|
|
export type RawSearchParams = Record<string, string | string[] | undefined>;
|
|
|
|
const SORTS = ['newest', 'price_asc', 'price_desc', 'best_selling', 'relevance'] as const;
|
|
|
|
export function parseListingParams(params: RawSearchParams): ProductListQuery {
|
|
return {
|
|
q: single(params['q']),
|
|
colors: list(params['colors']),
|
|
sizes: list(params['sizes']),
|
|
brandSlugs: list(params['brandSlugs']),
|
|
categorySlug: single(params['category']),
|
|
onSale: single(params['onSale']) === 'true' ? true : undefined,
|
|
sort: parseSort(single(params['sort'])),
|
|
// Present only when someone followed the "load more" link without
|
|
// JavaScript, or a crawler did. The client path appends in place and never
|
|
// puts a cursor in the address bar.
|
|
cursor: single(params['cursor']),
|
|
};
|
|
}
|
|
|
|
function single(value: string | string[] | undefined): string | undefined {
|
|
const raw = Array.isArray(value) ? value[0] : value;
|
|
return raw && raw.length > 0 ? raw : undefined;
|
|
}
|
|
|
|
/** Accepts both `?colors=a,b` and repeated `?colors=a&colors=b`. */
|
|
function list(value: string | string[] | undefined): string[] | undefined {
|
|
if (!value) return undefined;
|
|
|
|
const items = (Array.isArray(value) ? value : [value])
|
|
.flatMap((entry) => entry.split(','))
|
|
.map((entry) => entry.trim())
|
|
.filter(Boolean);
|
|
|
|
return items.length > 0 ? items : undefined;
|
|
}
|
|
|
|
function parseSort(value: string | undefined): ProductListQuery['sort'] {
|
|
return SORTS.includes(value as (typeof SORTS)[number])
|
|
? (value as ProductListQuery['sort'])
|
|
: undefined;
|
|
}
|