diff --git a/src/products/components/ProductListPage/filters.ts b/src/products/components/ProductListPage/filters.ts index 14eefad06..cd1a5606d 100644 --- a/src/products/components/ProductListPage/filters.ts +++ b/src/products/components/ProductListPage/filters.ts @@ -2,6 +2,7 @@ import { IFilter } from "@saleor/components/Filter"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { AttributeInputTypeEnum, StockAvailability } from "@saleor/graphql"; import { commonMessages, sectionNames } from "@saleor/intl"; +import { ProductListUrlFiltersAsDictWithMultipleValues } from "@saleor/products/urls"; import { AutocompleteFilterOpts, FilterOpts, @@ -20,17 +21,18 @@ import { } from "@saleor/utils/filters/fields"; import { defineMessages, IntlShape } from "react-intl"; -export enum ProductFilterKeys { - attributes = "attributes", - categories = "categories", - collections = "collections", - metadata = "metadata", - price = "price", - productType = "productType", - stock = "stock", - channel = "channel", - productKind = "productKind", -} +export const ProductFilterKeys = { + ...ProductListUrlFiltersAsDictWithMultipleValues, + categories: "categories", + collections: "collections", + metadata: "metadata", + price: "price", + productType: "productType", + stock: "stock", + channel: "channel", + productKind: "productKind", +} as const; +export type ProductFilterKeys = typeof ProductFilterKeys[keyof typeof ProductFilterKeys]; export type AttributeFilterOpts = FilterOpts & { id: string; @@ -260,7 +262,7 @@ export function createFilterStructure( }, ), active: attr.active, - group: ProductFilterKeys.attributes, + group: ProductFilterKeys.booleanAttributes, })), ...dateAttributes.map(attr => ({ ...createDateField(attr.slug, attr.name, { @@ -268,7 +270,7 @@ export function createFilterStructure( max: attr.value[1] ?? attr.value[0], }), active: attr.active, - group: ProductFilterKeys.attributes, + group: ProductFilterKeys.dateAttributes, })), ...dateTimeAttributes.map(attr => ({ ...createDateTimeField(attr.slug, attr.name, { @@ -276,7 +278,7 @@ export function createFilterStructure( max: attr.value[1] ?? attr.value[0], }), active: attr.active, - group: ProductFilterKeys.attributes, + group: ProductFilterKeys.dateTimeAttributes, })), ...numericAttributes.map(attr => ({ ...createNumberField(attr.slug, attr.name, { @@ -284,7 +286,7 @@ export function createFilterStructure( max: attr.value[1] ?? attr.value[0], }), active: attr.active, - group: ProductFilterKeys.attributes, + group: ProductFilterKeys.numericAttributes, })), ...defaultAttributes.map(attr => ({ ...createAutocompleteField( @@ -304,7 +306,7 @@ export function createFilterStructure( attr.id, ), active: attr.active, - group: ProductFilterKeys.attributes, + group: ProductFilterKeys.stringAttributes, })), ]; } diff --git a/src/products/urls.ts b/src/products/urls.ts index 7f422ebc5..3678000f7 100644 --- a/src/products/urls.ts +++ b/src/products/urls.ts @@ -42,9 +42,14 @@ export enum ProductListUrlFiltersWithMultipleValues { collections = "collections", productTypes = "productTypes", } -export enum ProductListUrlFiltersAsDictWithMultipleValues { - attributes = "attributes", -} +export const ProductListUrlFiltersAsDictWithMultipleValues = { + booleanAttributes: "boolean-attributes", + dateAttributes: "date-attributes", + dateTimeAttributes: "datetime-attributes", + numericAttributes: "numeric-attributes", + stringAttributes: "string-attributes", +} as const; +export type ProductListUrlFiltersAsDictWithMultipleValues = typeof ProductListUrlFiltersAsDictWithMultipleValues[keyof typeof ProductListUrlFiltersAsDictWithMultipleValues]; export enum ProductListUrlFiltersWithKeyValueValues { metadata = "metadata", } diff --git a/src/products/views/ProductList/__snapshots__/filters.test.ts.snap b/src/products/views/ProductList/__snapshots__/filters.test.ts.snap index 0b7a1daf5..6799c9b6d 100644 --- a/src/products/views/ProductList/__snapshots__/filters.test.ts.snap +++ b/src/products/views/ProductList/__snapshots__/filters.test.ts.snap @@ -2,7 +2,27 @@ exports[`Filtering URL params should not be empty if active filters are present 1`] = ` Object { - "attributes": Object { + "categories": Array [ + "878752", + ], + "channel": "default-channel", + "collections": Array [ + "Q29sbGVjdGlvbjoc", + ], + "metadata": Array [ + Object { + "key": "metadataKey", + "value": "metadataValue", + }, + ], + "priceFrom": "10", + "priceTo": "20", + "productKind": "NORMAL", + "productTypes": Array [ + "UHJvZHVjdFR5cGU6MQ==", + ], + "stockStatus": "IN_STOCK", + "string-attributes": Object { "author": Array [ "john-doe", false, @@ -52,27 +72,7 @@ Object { "m", ], }, - "categories": Array [ - "878752", - ], - "channel": "default-channel", - "collections": Array [ - "Q29sbGVjdGlvbjoc", - ], - "metadata": Array [ - Object { - "key": "metadataKey", - "value": "metadataValue", - }, - ], - "priceFrom": "10", - "priceTo": "20", - "productKind": "NORMAL", - "productTypes": Array [ - "UHJvZHVjdFR5cGU6MQ==", - ], - "stockStatus": "IN_STOCK", } `; -exports[`Filtering URL params should not be empty if active filters are present 2`] = `"channel=default-channel&metadata%5B0%5D%5Bkey%5D=metadataKey&metadata%5B0%5D%5Bvalue%5D=metadataValue&productKind=NORMAL&stockStatus=IN_STOCK&priceFrom=10&priceTo=20&categories%5B0%5D=878752&collections%5B0%5D=Q29sbGVjdGlvbjoc&productTypes%5B0%5D=UHJvZHVjdFR5cGU6MQ%3D%3D&attributes%5Bauthor%5D%5B0%5D=john-doe&attributes%5Bauthor%5D%5B1%5D=false&attributes%5Bbox-size%5D%5B0%5D=100g&attributes%5Bbox-size%5D%5B1%5D=500g&attributes%5Bbrand%5D%5B0%5D=saleor&attributes%5Bbrand%5D%5B1%5D=false&attributes%5Bcandy-box-size%5D%5B0%5D=100g&attributes%5Bcandy-box-size%5D%5B1%5D=500g&attributes%5Bcoffee-genre%5D%5B0%5D=arabica&attributes%5Bcoffee-genre%5D%5B1%5D=false&attributes%5Bcollar%5D%5B0%5D=round&attributes%5Bcollar%5D%5B1%5D=polo&attributes%5Bcolor%5D%5B0%5D=blue&attributes%5Bcolor%5D%5B1%5D=false&attributes%5Bcover%5D%5B0%5D=soft&attributes%5Bcover%5D%5B1%5D=middle-soft&attributes%5Bflavor%5D%5B0%5D=sour&attributes%5Bflavor%5D%5B1%5D=false&attributes%5Blanguage%5D%5B0%5D=english&attributes%5Blanguage%5D%5B1%5D=false&attributes%5Bpublisher%5D%5B0%5D=mirumee-press&attributes%5Bpublisher%5D%5B1%5D=false&attributes%5Bsize%5D%5B0%5D=xs&attributes%5Bsize%5D%5B1%5D=m"`; +exports[`Filtering URL params should not be empty if active filters are present 2`] = `"channel=default-channel&metadata%5B0%5D%5Bkey%5D=metadataKey&metadata%5B0%5D%5Bvalue%5D=metadataValue&productKind=NORMAL&stockStatus=IN_STOCK&priceFrom=10&priceTo=20&categories%5B0%5D=878752&collections%5B0%5D=Q29sbGVjdGlvbjoc&productTypes%5B0%5D=UHJvZHVjdFR5cGU6MQ%3D%3D&string-attributes%5Bauthor%5D%5B0%5D=john-doe&string-attributes%5Bauthor%5D%5B1%5D=false&string-attributes%5Bbox-size%5D%5B0%5D=100g&string-attributes%5Bbox-size%5D%5B1%5D=500g&string-attributes%5Bbrand%5D%5B0%5D=saleor&string-attributes%5Bbrand%5D%5B1%5D=false&string-attributes%5Bcandy-box-size%5D%5B0%5D=100g&string-attributes%5Bcandy-box-size%5D%5B1%5D=500g&string-attributes%5Bcoffee-genre%5D%5B0%5D=arabica&string-attributes%5Bcoffee-genre%5D%5B1%5D=false&string-attributes%5Bcollar%5D%5B0%5D=round&string-attributes%5Bcollar%5D%5B1%5D=polo&string-attributes%5Bcolor%5D%5B0%5D=blue&string-attributes%5Bcolor%5D%5B1%5D=false&string-attributes%5Bcover%5D%5B0%5D=soft&string-attributes%5Bcover%5D%5B1%5D=middle-soft&string-attributes%5Bflavor%5D%5B0%5D=sour&string-attributes%5Bflavor%5D%5B1%5D=false&string-attributes%5Blanguage%5D%5B0%5D=english&string-attributes%5Blanguage%5D%5B1%5D=false&string-attributes%5Bpublisher%5D%5B0%5D=mirumee-press&string-attributes%5Bpublisher%5D%5B1%5D=false&string-attributes%5Bsize%5D%5B0%5D=xs&string-attributes%5Bsize%5D%5B1%5D=m"`; diff --git a/src/products/views/ProductList/filters.test.ts b/src/products/views/ProductList/filters.test.ts index 6f5eaa179..0c1c081ff 100644 --- a/src/products/views/ProductList/filters.test.ts +++ b/src/products/views/ProductList/filters.test.ts @@ -1,4 +1,4 @@ -import { StockAvailability } from "@saleor/graphql"; +import { AttributeInputTypeEnum, StockAvailability } from "@saleor/graphql"; import { createFilterStructure } from "@saleor/products/components/ProductListPage"; import { ProductListUrlFilters } from "@saleor/products/urls"; import { getFilterQueryParams } from "@saleor/utils/filters"; @@ -7,7 +7,15 @@ import { getExistingKeys, setFilterOptsStatus } from "@test/filters"; import { config } from "@test/intl"; import { createIntl } from "react-intl"; -import { getFilterQueryParam, getFilterVariables } from "./filters"; +import { ProductListUrlFiltersAsDictWithMultipleValues } from "../../urls"; +import { + FilterParam, + getAttributeValuesFromParams, + getFilterQueryParam, + getFilterVariables, + mapAttributeParamsToFilterOpts, + parseFilterValue, +} from "./filters"; import { productListFilterOpts } from "./fixtures"; describe("Filtering query params", () => { @@ -31,6 +39,150 @@ describe("Filtering query params", () => { }); }); +describe("Get attribute values from URL params", () => { + type GetAttributeValuesFromParams = Parameters< + typeof getAttributeValuesFromParams + >; + + it("should return empty array when attribute doesn't exist in params", () => { + // Arrange + const params: GetAttributeValuesFromParams[0] = {}; + const attribute: GetAttributeValuesFromParams[1] = { + slug: "test", + inputType: AttributeInputTypeEnum.DROPDOWN, + }; + + // Act + const attributeValues = getAttributeValuesFromParams(params, attribute); + + // Assert + expect(attributeValues).toHaveLength(0); + }); + + it("should return attribute values when attribute exists in params", () => { + // Arrange + const params: GetAttributeValuesFromParams[0] = { + "string-attributes": { + test: ["value-1", "value-2"], + }, + }; + const attribute: GetAttributeValuesFromParams[1] = { + slug: "test", + inputType: AttributeInputTypeEnum.DROPDOWN, + }; + + // Act + const attributeValues = getAttributeValuesFromParams(params, attribute); + + // Assert + expect(attributeValues).toEqual(["value-1", "value-2"]); + }); +}); + +describe("Map attribute params to filter opts", () => { + type MapAttributeParamsToFilterOpts = Parameters< + typeof mapAttributeParamsToFilterOpts + >; + type MapAttributeParamsToFilterOptsReturn = ReturnType< + typeof mapAttributeParamsToFilterOpts + >; + + it("should return empty array when no params given", () => { + // Arrange + const attributes: MapAttributeParamsToFilterOpts[0] = [ + { + id: "1", + slug: "test", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Test", + __typename: "Attribute", + }, + ]; + const params: MapAttributeParamsToFilterOpts[1] = {}; + + // Act + const filterOpts = mapAttributeParamsToFilterOpts(attributes, params); + + // Assert + const expectedFilterOpts: MapAttributeParamsToFilterOptsReturn = [ + { + id: "1", + slug: "test", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Test", + active: false, + value: [], + }, + ]; + expect(filterOpts).toEqual(expectedFilterOpts); + }); + + it("should return filter opts with proper values selected according to passed values selection in params", () => { + // Arrange + const attributes: MapAttributeParamsToFilterOpts[0] = [ + { + id: "1", + slug: "test-1", + inputType: AttributeInputTypeEnum.MULTISELECT, + name: "Test 1", + __typename: "Attribute", + }, + { + id: "2", + slug: "test-2", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Test 2", + __typename: "Attribute", + }, + { + id: "3", + slug: "test-3", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Test 3", + __typename: "Attribute", + }, + ]; + const params: MapAttributeParamsToFilterOpts[1] = { + "string-attributes": { + "test-1": ["value-1", "value-2"], + "test-2": ["value-3"], + }, + }; + + // Act + const filterOpts = mapAttributeParamsToFilterOpts(attributes, params); + + // Assert + const expectedFilterOpts: MapAttributeParamsToFilterOptsReturn = [ + { + id: "1", + slug: "test-1", + inputType: AttributeInputTypeEnum.MULTISELECT, + name: "Test 1", + active: true, + value: ["value-1", "value-2"], + }, + { + id: "2", + slug: "test-2", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Test 2", + active: true, + value: ["value-3"], + }, + { + id: "3", + slug: "test-3", + inputType: AttributeInputTypeEnum.DROPDOWN, + name: "Test 3", + active: false, + value: [], + }, + ]; + expect(filterOpts).toEqual(expectedFilterOpts); + }); +}); + describe("Filtering URL params", () => { const intl = createIntl(config); @@ -55,3 +207,161 @@ describe("Filtering URL params", () => { expect(stringifyQs(filterQueryParams)).toMatchSnapshot(); }); }); + +describe("Parsing filter value", () => { + it("should return boolean values when boolean attributes values passed", () => { + // Arrange + const params: ProductListUrlFilters = { + "boolean-attributes": { + "test-1": ["true"], + "test-2": ["false"], + }, + }; + const type = + ProductListUrlFiltersAsDictWithMultipleValues.booleanAttributes; + + // Act + const parsedValue1 = parseFilterValue(params, "test-1", type); + const parsedValue2 = parseFilterValue(params, "test-2", type); + + // Assert + const expectedValue1: FilterParam = { + slug: "test-1", + boolean: true, + }; + const expectedValue2: FilterParam = { + slug: "test-2", + boolean: false, + }; + expect(parsedValue1).toEqual(expectedValue1); + expect(parsedValue2).toEqual(expectedValue2); + }); + + it("should return numeric values when numeric attributes values passed", () => { + // Arrange + const params: ProductListUrlFilters = { + "numeric-attributes": { + "test-1": ["1"], + "test-2": ["1", "2"], + }, + }; + const type = + ProductListUrlFiltersAsDictWithMultipleValues.numericAttributes; + + // Act + const parsedValue1 = parseFilterValue(params, "test-1", type); + const parsedValue2 = parseFilterValue(params, "test-2", type); + + // Assert + const expectedValue1: FilterParam = { + slug: "test-1", + valuesRange: { + gte: 1, + lte: 1, + }, + }; + const expectedValue2: FilterParam = { + slug: "test-2", + valuesRange: { + gte: 1, + lte: 2, + }, + }; + expect(parsedValue1).toEqual(expectedValue1); + expect(parsedValue2).toEqual(expectedValue2); + }); + + it("should return string values when string attributes values passed", () => { + // Arrange + const params: ProductListUrlFilters = { + "string-attributes": { + "test-1": ["value-1"], + "test-2": ["value-2", "value-3"], + }, + }; + const type = ProductListUrlFiltersAsDictWithMultipleValues.stringAttributes; + + // Act + const parsedValue1 = parseFilterValue(params, "test-1", type); + const parsedValue2 = parseFilterValue(params, "test-2", type); + + // Assert + const expectedValue1: FilterParam = { + slug: "test-1", + values: ["value-1"], + }; + const expectedValue2: FilterParam = { + slug: "test-2", + values: ["value-2", "value-3"], + }; + expect(parsedValue1).toEqual(expectedValue1); + expect(parsedValue2).toEqual(expectedValue2); + }); + + it("should return date values when date attributes values passed", () => { + // Arrange + const params: ProductListUrlFilters = { + "date-attributes": { + "test-1": ["2020-01-01"], + "test-2": ["2020-01-01", "2020-02-02"], + }, + }; + const type = ProductListUrlFiltersAsDictWithMultipleValues.dateAttributes; + + // Act + const parsedValue1 = parseFilterValue(params, "test-1", type); + const parsedValue2 = parseFilterValue(params, "test-2", type); + + // Assert + const expectedValue1: FilterParam = { + slug: "test-1", + date: { + gte: "2020-01-01", + lte: "2020-01-01", + }, + }; + const expectedValue2: FilterParam = { + slug: "test-2", + date: { + gte: "2020-01-01", + lte: "2020-02-02", + }, + }; + expect(parsedValue1).toEqual(expectedValue1); + expect(parsedValue2).toEqual(expectedValue2); + }); + + it("should return datetime values when datetime attributes values passed", () => { + // Arrange + const params: ProductListUrlFilters = { + "datetime-attributes": { + "test-1": ["2020-01-01T00:00:00"], + "test-2": ["2020-01-01T00:00:00", "2020-02-02T00:00:00"], + }, + }; + const type = + ProductListUrlFiltersAsDictWithMultipleValues.dateTimeAttributes; + + // Act + const parsedValue1 = parseFilterValue(params, "test-1", type); + const parsedValue2 = parseFilterValue(params, "test-2", type); + + // Assert + const expectedValue1: FilterParam = { + slug: "test-1", + dateTime: { + gte: "2020-01-01T00:00:00", + lte: "2020-01-01T00:00:00", + }, + }; + const expectedValue2: FilterParam = { + slug: "test-2", + dateTime: { + gte: "2020-01-01T00:00:00", + lte: "2020-02-02T00:00:00", + }, + }; + expect(parsedValue1).toEqual(expectedValue1); + expect(parsedValue2).toEqual(expectedValue2); + }); +}); diff --git a/src/products/views/ProductList/filters.ts b/src/products/views/ProductList/filters.ts index 5498c5d6e..d9994ef50 100644 --- a/src/products/views/ProductList/filters.ts +++ b/src/products/views/ProductList/filters.ts @@ -1,5 +1,7 @@ import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { + AttributeFragment, + AttributeInputTypeEnum, InitialProductFilterAttributesQuery, InitialProductFilterCategoriesQuery, InitialProductFilterCollectionsQuery, @@ -27,7 +29,6 @@ import { mapNodeToChoice, mapSlugNodeToChoice, } from "@saleor/utils/maps"; -import moment from "moment-timezone"; import { FilterElement, @@ -57,6 +58,52 @@ import { import { getProductGiftCardFilterParam } from "./utils"; export const PRODUCT_FILTERS_KEY = "productFilters"; +function getAttributeFilterParamType(inputType: AttributeInputTypeEnum) { + switch (inputType) { + case AttributeInputTypeEnum.DATE: + return ProductListUrlFiltersAsDictWithMultipleValues.dateAttributes; + case AttributeInputTypeEnum.DATE_TIME: + return ProductListUrlFiltersAsDictWithMultipleValues.dateTimeAttributes; + case AttributeInputTypeEnum.NUMERIC: + return ProductListUrlFiltersAsDictWithMultipleValues.numericAttributes; + case AttributeInputTypeEnum.BOOLEAN: + return ProductListUrlFiltersAsDictWithMultipleValues.booleanAttributes; + default: + return ProductListUrlFiltersAsDictWithMultipleValues.stringAttributes; + } +} + +export function getAttributeValuesFromParams( + params: ProductListUrlFilters, + attribute: Pick, +) { + return ( + params[getAttributeFilterParamType(attribute.inputType)]?.[ + attribute.slug + ] || [] + ); +} + +export function mapAttributeParamsToFilterOpts( + attributes: RelayToFlat, + params: ProductListUrlFilters, +) { + return attributes + .sort((a, b) => (a.name > b.name ? 1 : -1)) + .map(attr => { + const attrValues = getAttributeValuesFromParams(params, attr); + + return { + active: attrValues.length > 0, + id: attr.id, + name: attr.name, + slug: attr.slug, + inputType: attr.inputType, + value: dedupeFilter(attrValues), + }; + }); +} + export function getFilterOpts( params: ProductListUrlFilters, attributes: RelayToFlat, @@ -89,16 +136,7 @@ export function getFilterOpts( channels: SingleAutocompleteChoiceType[], ): ProductListFilterOpts { return { - attributes: attributes - .sort((a, b) => (a.name > b.name ? 1 : -1)) - .map(attr => ({ - active: maybe(() => params.attributes[attr.slug].length > 0, false), - id: attr.id, - name: attr.name, - slug: attr.slug, - inputType: attr.inputType, - value: dedupeFilter(params.attributes?.[attr.slug] || []), - })), + attributes: mapAttributeParamsToFilterOpts(attributes, params), attributeChoices: { active: true, choices: mapSlugNodeToChoice( @@ -233,40 +271,6 @@ export function getFilterOpts( }; } -const parseFilterValue = ( - params: ProductListUrlFilters, - key: string, -): { - type: "boolean" | "date" | "dateTime" | "numeric" | "string"; - isMulti: boolean; - value: string[]; -} => { - const value = params.attributes[key]; - const isMulti = params.attributes[key].length > 1; - - const isBooleanValue = value.every(val => val === "true" || val === "false"); - const isDateValue = (isMulti ? value : [value]).some(val => - moment(val, moment.HTML5_FMT.DATE, true).isValid(), - ); - const isDateTimeValue = (isMulti ? value : [value]).some(val => - moment(val, moment.ISO_8601, true).isValid(), - ); - const isNumericValue = value.some(value => !isNaN(parseFloat(value))); - - const data = { isMulti, value }; - - if (isBooleanValue) { - return { ...data, type: "boolean" }; - } else if (isDateValue) { - return { ...data, type: "date" }; - } else if (isDateTimeValue) { - return { ...data, type: "dateTime" }; - } else if (isNumericValue) { - return { ...data, type: "numeric" }; - } - return { ...data, type: "string" }; -}; - interface BaseFilterParam { slug: string; } @@ -282,57 +286,84 @@ interface DateTimeFilterParam extends BaseFilterParam { interface DefaultFilterParam extends BaseFilterParam { values: string[]; } +interface NumericFilterParam extends BaseFilterParam { + valuesRange: GteLte; +} +export type FilterParam = + | BooleanFilterParam + | DateFilterParam + | DateTimeFilterParam + | DefaultFilterParam + | NumericFilterParam; + +export const parseFilterValue = ( + params: ProductListUrlFilters, + key: string, + type: ProductListUrlFiltersAsDictWithMultipleValues, +): FilterParam => { + const value = params[type][key]; + const isMulti = params[type][key].length > 1; + + const name = { slug: key }; + + switch (type) { + case ProductListUrlFiltersAsDictWithMultipleValues.booleanAttributes: + return { ...name, boolean: JSON.parse(value[0]) }; + case ProductListUrlFiltersAsDictWithMultipleValues.dateAttributes: + return { + ...name, + date: getGteLteVariables({ + gte: value[0] || null, + lte: isMulti ? value[1] || null : value[0], + }), + }; + case ProductListUrlFiltersAsDictWithMultipleValues.dateTimeAttributes: + return { + ...name, + dateTime: getGteLteVariables({ + gte: value[0] || null, + lte: isMulti ? value[1] || null : value[0], + }), + }; + case ProductListUrlFiltersAsDictWithMultipleValues.numericAttributes: + const [gte, lte] = value.map(v => parseFloat(v)); + + return { + ...name, + valuesRange: { + gte: gte || undefined, + lte: isMulti ? lte || undefined : gte || undefined, + }, + }; + default: + return { ...name, values: value }; + } +}; function getFilteredAttributeValue( params: ProductListUrlFilters, -): Array< - | BooleanFilterParam - | BaseFilterParam - | DateTimeFilterParam - | DateFilterParam - | DefaultFilterParam -> { - return !!params.attributes - ? Object.keys(params.attributes).map(key => { - const { isMulti, type, value } = parseFilterValue(params, key); - const name = { slug: key }; +): FilterParam[] { + const attrValues = Object.values( + ProductListUrlFiltersAsDictWithMultipleValues, + ).reduce((attrValues, attributeType) => { + const attributes = params[attributeType]; - switch (type) { - case "boolean": - return { ...name, boolean: JSON.parse(value[0]) }; + if (!attributes) { + return attrValues; + } - case "date": - return { - ...name, - date: getGteLteVariables({ - gte: value[0] || null, - lte: isMulti ? value[1] || null : value[0], - }), - }; + return [ + ...attrValues, + ...Object.keys(attributes).map(key => + parseFilterValue(params, key, attributeType), + ), + ]; + }, []); - case "dateTime": - return { - ...name, - dateTime: getGteLteVariables({ - gte: value[0] || null, - lte: isMulti ? value[1] || null : value[0], - }), - }; - - case "numeric": - return { - ...name, - valuesRange: { - gte: value[0] || undefined, - lte: isMulti ? value[1] || undefined : value[0] || undefined, - }, - }; - - default: - return { ...name, values: value }; - } - }) - : null; + if (!attrValues.length) { + return null; + } + return attrValues; } export function getFilterVariables( @@ -408,7 +439,7 @@ export function getFilterQueryParam( case ProductFilterKeys.stock: return getSingleEnumValueQueryParam( - filter as FilterElementRegular, + filter as FilterElementRegular, ProductListUrlFiltersEnum.stockStatus, StockAvailability, );