diff --git a/packs/vtex/loaders/legacy/productListingPage.ts b/packs/vtex/loaders/legacy/productListingPage.ts index 02786552..1f313e4a 100644 --- a/packs/vtex/loaders/legacy/productListingPage.ts +++ b/packs/vtex/loaders/legacy/productListingPage.ts @@ -19,6 +19,7 @@ import { } from "deco-sites/std/packs/vtex/utils/segment.ts"; import { legacyFacetToFilter, + normalizeFq, toProduct, } from "deco-sites/std/packs/vtex/utils/transform.ts"; import { fetchAPI, fetchSafe } from "deco-sites/std/utils/fetchVTEX.ts"; @@ -132,6 +133,8 @@ const loader = async ( const ft = props.ft || url.searchParams.get("ft") || url.searchParams.get("q") || ""; const fq = props.fq || url.searchParams.get("fq") || ""; + const normalizedFq = normalizeFq(fq); + const hasPreSelectedFq = props.fq; const _from = `${page * count}`; const _to = `${(page + 1) * count - 1}`; @@ -146,7 +149,7 @@ const loader = async ( ? getMapAndTerm(pageTypes) : [maybeMap, maybeTerm]; const fmap = url.searchParams.get("fmap") ?? map; - const args = { map, _from, _to, O, ft, fq }; + const args = { map, _from, _to, O, ft, fq: normalizedFq }; const pParams = new URLSearchParams(params); Object.entries(args).map(([key, value]) => value && pParams.set(key, value)); @@ -185,9 +188,10 @@ const loader = async ( const filters = Object.entries({ Departments: vtexFacets.Departments, Brands: vtexFacets.Brands, + PriceRanges: !hasPreSelectedFq ? vtexFacets.PriceRanges : [], ...vtexFacets.SpecificationFilters, }).map(([name, facets]) => - legacyFacetToFilter(name, facets, url, map, filtersBehavior) + legacyFacetToFilter(name, facets, url, map, fq, filtersBehavior) ) .flat() .filter((x): x is Filter => Boolean(x)); diff --git a/packs/vtex/types.ts b/packs/vtex/types.ts index 31a8e5a9..a909954f 100644 --- a/packs/vtex/types.ts +++ b/packs/vtex/types.ts @@ -663,6 +663,7 @@ export type LegacyProduct = IProduct & { export type LegacyFacets = { Departments: LegacyFacet[]; Brands: LegacyFacet[]; + PriceRanges: LegacyFacet[]; SpecificationFilters: Record; }; @@ -702,6 +703,7 @@ export interface LegacyFacet { LinkEncoded: string; Map: string; Value: string; + Slug: string; Children: LegacyFacet[]; } diff --git a/packs/vtex/utils/transform.ts b/packs/vtex/utils/transform.ts index 3458a1a7..cabbf5cf 100644 --- a/packs/vtex/utils/transform.ts +++ b/packs/vtex/utils/transform.ts @@ -477,6 +477,7 @@ export const legacyFacetToFilter = ( facets: LegacyFacet[], url: URL, map: string, + fq: string, behavior: "dynamic" | "static", ): Filter | null => { const mapSegments = map.split(","); @@ -487,12 +488,84 @@ export const legacyFacetToFilter = ( const mapSet = new Set(mapSegments); const pathSet = new Set(pathSegments); + const isPriceRangeSelected = (facet: LegacyFacet) => { + const isCurrentPriceRange = fq.startsWith("P:[") && fq.includes("+TO+"); + + const isPriceRangeFacet = name == "PriceRanges"; + + const [currentMin, currentMax] = isCurrentPriceRange + ? getMinAndMaxPrices(fq) + : [0, 0]; + const [facetMin, facetMax] = isPriceRangeFacet + ? getMinAndMaxPrices(facet.Slug) + : [0, 0]; + + const isSelectedPrice = isCurrentPriceRange && (currentMin == facetMin) && + (currentMax == facetMax); + + return isSelectedPrice; + }; + + const getMinAndMaxPrices = (param: string) => { + const separator1 = "P:["; + const separator2 = "+TO+"; + const separator3 = "]"; + + const separator4 = "de-"; + const separator5 = "-a-"; + + if (param.startsWith(separator1) && param.endsWith(separator3)) { + const values = param.substring( + separator1.length, + param.length - separator3.length, + ).split(separator2); + + if (values.length === 2) { + const min = parseFloat(values[0]); + const max = parseFloat(values[1]); + + const isValidNumbers = !isNaN(min) && !isNaN(max); + if (isValidNumbers) { + return [min, max]; + } + } + } + + if (param.startsWith(separator4) && param.includes(separator5)) { + const values = param.substring(separator4.length).split(separator5); + if (values.length === 2) { + const min = parseFloat(values[0]); + const max = parseFloat(values[1]); + + const isValidNumbers = !isNaN(min) && !isNaN(max); + if (isValidNumbers) { + return [min, max]; + } + } + } + + return [0, 0]; + }; + const getLink = (facet: LegacyFacet, selected: boolean) => { // Do not allow removing root facet to avoid going back to home page if (mapSegments.length === 1) { return `${url.pathname}${url.search}`; } + if (name == "PriceRanges") { + const link = new URL(`/${pathSegments.join("/")}`, url); + link.searchParams.set("map", mapSegments.join(",")); + + const [min, max] = getMinAndMaxPrices(facet.Slug); + + if (min && max && !selected) { + link.searchParams.set("fq", `P:[${min}+TO+${max}]`); + } + + return `${link.pathname}${link.search}`; + } + const index = pathSegments.findIndex((s) => s === facet.Value); const newMap = selected ? [...mapSegments.filter((_, i) => i !== index)] @@ -507,6 +580,10 @@ export const legacyFacetToFilter = ( link.searchParams.set("fmap", url.searchParams.get("fmap") || map); } + if (fq) { + link.searchParams.set("fq", fq); + } + return `${link.pathname}${link.search}`; }; @@ -516,7 +593,8 @@ export const legacyFacetToFilter = ( label: name, key: name, values: facets.map((facet) => { - const selected = mapSet.has(facet.Map) && pathSet.has(facet.Value); + const selected = (mapSet.has(facet.Map) && pathSet.has(facet.Value)) || + isPriceRangeSelected(facet); return ({ value: facet.Value, @@ -643,3 +721,9 @@ function nodeToNavbar(node: Category): Navbar { export const categoryTreeToNavbar = (tree: Category[]): Navbar[] => tree.map(nodeToNavbar); + +export const normalizeFq = (fq: string) => { + const decodedFq = decodeURI(fq); + const fqWithoutSpaces = decodedFq.split("+").join("%20"); + return fqWithoutSpaces; +};