diff --git a/.changeset/quiet-wolves-boil.md b/.changeset/quiet-wolves-boil.md new file mode 100644 index 000000000..02f0eb8bd --- /dev/null +++ b/.changeset/quiet-wolves-boil.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": minor +--- + +Add all static fields for product filtering diff --git a/src/components/ConditionalFilter/API/Handler.ts b/src/components/ConditionalFilter/API/Handler.ts index 6d1049d4b..fe34a548b 100644 --- a/src/components/ConditionalFilter/API/Handler.ts +++ b/src/components/ConditionalFilter/API/Handler.ts @@ -24,8 +24,6 @@ import { ItemOption } from "../FilterElement/ConditionValue"; import { LeftOperand } from "../LeftOperandsProvider"; export interface Handler { - client: ApolloClient; - query: string; fetch: () => Promise; } @@ -167,3 +165,11 @@ export class AttributesHandler implements Handler { ); }; } + +export class BooleanValuesHandler implements Handler { + constructor(public options: LeftOperand[]) {} + + fetch = async (): Promise => { + return this.options + }; +} diff --git a/src/components/ConditionalFilter/API/InitialStateResponse.ts b/src/components/ConditionalFilter/API/InitialStateResponse.ts index 496c5f4f1..33efab3dd 100644 --- a/src/components/ConditionalFilter/API/InitialStateResponse.ts +++ b/src/components/ConditionalFilter/API/InitialStateResponse.ts @@ -15,24 +15,34 @@ export interface InitialState { attribute: Record; channel: ItemOption[]; collection: ItemOption[]; - producttype: ItemOption[]; + productType: ItemOption[]; + isAvailable: ItemOption[]; + isPublished: ItemOption[]; + isVisibleInListing: ItemOption[]; + hasCategory: ItemOption[]; + giftCard: ItemOption[]; } export class InitialStateResponse implements InitialState { constructor( - public category: ItemOption[], - public attribute: Record, - public channel: ItemOption[], - public collection: ItemOption[], - public producttype: ItemOption[], - ) {} + public category: ItemOption[] = [], + public attribute: Record = {}, + public channel: ItemOption[] = [], + public collection: ItemOption[] = [], + public productType: ItemOption[] = [], + public isAvailable: ItemOption[] = [], + public isPublished: ItemOption[] = [], + public isVisibleInListing: ItemOption[] = [], + public hasCategory: ItemOption[] = [], + public giftCard: ItemOption[] = [] + ) { } public attributeByName(name: string) { return this.attribute[name]; } public static empty() { - return new InitialStateResponse([], {}, [], [], []); + return new InitialStateResponse(); } public filterByUrlToken(token: UrlToken) { @@ -57,10 +67,20 @@ export class InitialStateResponse implements InitialState { return this.category; case "collection": return this.collection; - case "producttype": - return this.producttype; + case "productType": + return this.productType; case "channel": return this.channel; + case "isAvailable": + return this.isAvailable; + case "isPublished": + return this.isPublished; + case "isVisibleInListing": + return this.isVisibleInListing; + case "hasCategory": + return this.hasCategory; + case "giftCard": + return this.giftCard; default: return []; } diff --git a/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx b/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx index 0ccc5436b..b75d92b4d 100644 --- a/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx +++ b/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx @@ -1,10 +1,12 @@ import { ApolloClient, useApolloClient } from "@apollo/client"; +import { RowType } from "../constants"; import { FilterContainer, FilterElement } from "../FilterElement"; import { FilterAPIProvider } from "./FilterAPIProvider"; import { AttributeChoicesHandler, AttributesHandler, + BooleanValuesHandler, CategoryHandler, ChannelHandler, CollectionHandler, @@ -27,12 +29,24 @@ const getFilterElement = ( throw new Error("Unknown filter element used to create API handler"); }; +const isStaticBoolean = (rowType: RowType) => { + return [ + "isAvailable", + "isPublished", + "isVisibleInListing", + "hasCategory", + "giftCard" + ].includes(rowType) +} + const createAPIHandler = ( selectedRow: FilterElement, client: ApolloClient, inputValue: string, ): Handler => { - if (selectedRow.isAttribute()) { + const rowType = selectedRow.rowType() + + if (rowType === "attribute") { return new AttributeChoicesHandler( client, selectedRow.value.value, @@ -40,19 +54,36 @@ const createAPIHandler = ( ); } - if (selectedRow.isCollection()) { + if (rowType && isStaticBoolean(rowType)) { + return new BooleanValuesHandler([ + { + label: "Yes", + value: "true", + type: rowType, + slug: "true" + }, + { + label: "No", + value: "false", + type: rowType, + slug: "false" + } + ]) + } + + if (rowType === "collection") { return new CollectionHandler(client, inputValue); } - if (selectedRow.isCategory()) { + if (rowType === "category") { return new CategoryHandler(client, inputValue); } - if (selectedRow.isProductType()) { + if (rowType === "productType") { return new ProductTypeHandler(client, inputValue); } - if (selectedRow.isChannel()) { + if (rowType === "channel") { return new ChannelHandler(client, inputValue); } diff --git a/src/components/ConditionalFilter/API/initialState/helpers.ts b/src/components/ConditionalFilter/API/initialState/helpers.ts index 41b0c0ff9..5ee704964 100644 --- a/src/components/ConditionalFilter/API/initialState/helpers.ts +++ b/src/components/ConditionalFilter/API/initialState/helpers.ts @@ -7,6 +7,7 @@ import { _SearchProductTypesOperandsQuery, } from "@dashboard/graphql"; +import { createBooleanOptions } from "../../constants"; import { createOptionsFromAPI } from "../Handler"; import { InitialState } from "../InitialStateResponse"; import { InitialAPIResponse } from "./types"; @@ -70,7 +71,7 @@ export const createInitialStateFromData = ( if (isProductTypeQuery(query)) { return { ...acc, - producttype: createOptionsFromAPI( + productType: createOptionsFromAPI( query.data?.productTypes?.edges ?? [], ), }; @@ -102,7 +103,12 @@ export const createInitialStateFromData = ( channel: [], collection: [], category: [], - producttype: [], + productType: [], + isAvailable: createBooleanOptions(), + isPublished: createBooleanOptions(), + isVisibleInListing: createBooleanOptions(), + hasCategory: createBooleanOptions(), + giftCard: createBooleanOptions(), attribute: {}, }, ); diff --git a/src/components/ConditionalFilter/API/initialState/useInitialAPIState.tsx b/src/components/ConditionalFilter/API/initialState/useInitialAPIState.tsx index 449dbafb1..38a4a8415 100644 --- a/src/components/ConditionalFilter/API/initialState/useInitialAPIState.tsx +++ b/src/components/ConditionalFilter/API/initialState/useInitialAPIState.tsx @@ -40,7 +40,7 @@ export const useProductInitialAPIState = (): InitialAPIState => { const fetchQueries = async ({ category, collection, - producttype, + productType, channel, attribute, }: FetchingParams) => { @@ -85,7 +85,7 @@ export const useProductInitialAPIState = (): InitialAPIState => { ); } - if (producttype.length > 0) { + if (productType.length > 0) { queriesToRun.push( client.query< _SearchProductTypesOperandsQuery, @@ -93,8 +93,8 @@ export const useProductInitialAPIState = (): InitialAPIState => { >({ query: _SearchProductTypesOperandsDocument, variables: { - productTypesSlugs: producttype, - first: producttype.length, + productTypesSlugs: productType, + first: productType.length, }, }), ); @@ -124,7 +124,12 @@ export const useProductInitialAPIState = (): InitialAPIState => { initialState.attribute, initialState.channel, initialState.collection, - initialState.producttype, + initialState.productType, + initialState.isAvailable, + initialState.isPublished, + initialState.isVisibleInListing, + initialState.hasCategory, + initialState.giftCard ), ); setLoading(false); diff --git a/src/components/ConditionalFilter/FilterElement/FilterElement.ts b/src/components/ConditionalFilter/FilterElement/FilterElement.ts index 19dfb44ed..e7bc32e78 100644 --- a/src/components/ConditionalFilter/FilterElement/FilterElement.ts +++ b/src/components/ConditionalFilter/FilterElement/FilterElement.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/member-ordering */ import { InitialStateResponse } from "../API/InitialStateResponse"; +import { RowType, STATIC_OPTIONS } from "../constants"; import { LeftOperand } from "../LeftOperandsProvider"; import { TokenType, UrlEntry, UrlToken } from "./../ValueProvider/UrlToken"; import { Condition } from "./Condition"; @@ -23,7 +24,13 @@ class ExpressionValue { } public static fromUrlToken(token: UrlToken) { - return new ExpressionValue(token.name, token.name, token.name); + const option = STATIC_OPTIONS.find(o => o.slug === token.name) + + if (!option) { + return new ExpressionValue(token.name, token.name, token.name); + } + + return new ExpressionValue(token.name, option.label, token.name); } public static forAttribute( @@ -106,23 +113,18 @@ export class FilterElement { return ConditionOptions.isAttributeInputType(this.value.type); } - public isCollection() { - return this.value.value === "collection"; - } + public rowType (): RowType | null { + if (this.isStatic()) { + return this.value.value as RowType + } - public isCategory() { - return this.value.value === "category"; - } + if (this.isAttribute()) { + return "attribute" + } - public isProductType() { - return this.value.value === "producttype"; + return null; } - public isChannel() { - return this.value.value === "channel"; - } - - public asUrlEntry(): UrlEntry { if (this.isAttribute()) { return UrlEntry.forAttribute(this.condition.selected, this.value.value); diff --git a/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts b/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts index 758562af9..02c275113 100644 --- a/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts +++ b/src/components/ConditionalFilter/ValueProvider/TokenArray/fetchingParams.ts @@ -4,7 +4,7 @@ export interface FetchingParams { category: string[]; collection: string[]; channel: string[]; - producttype: string[]; + productType: string[]; attribute: Record; } @@ -14,7 +14,7 @@ export const emptyFetchingParams: FetchingParams = { category: [], collection: [], channel: [], - producttype: [], + productType: [], attribute: {}, }; diff --git a/src/components/ConditionalFilter/ValueProvider/UrlToken.ts b/src/components/ConditionalFilter/ValueProvider/UrlToken.ts index af64a4aab..5ead9fd90 100644 --- a/src/components/ConditionalFilter/ValueProvider/UrlToken.ts +++ b/src/components/ConditionalFilter/ValueProvider/UrlToken.ts @@ -5,7 +5,17 @@ import { slugFromConditionValue } from "../FilterElement/ConditionValue"; export const CONDITIONS = ["is", "equals", "in", "between", "lower", "greater"]; -const STATIC_TO_LOAD = ["category", "collection", "channel", "producttype"]; +const STATIC_TO_LOAD = [ + "category", + "collection", + "channel", + "productType", + "isAvailable", + "isPublished", + "isVisibleInListing", + "hasCategory", + "giftCard" +]; export const TokenType = { ATTRIBUTE: "a", diff --git a/src/components/ConditionalFilter/constants.ts b/src/components/ConditionalFilter/constants.ts index 32fd8f07c..4ab2253ae 100644 --- a/src/components/ConditionalFilter/constants.ts +++ b/src/components/ConditionalFilter/constants.ts @@ -1,3 +1,6 @@ +import { ItemOption } from "./FilterElement/ConditionValue"; +import { LeftOperand } from "./LeftOperandsProvider"; + export const STATIC_CONDITIONS = { category: [ { type: "combobox", label: "is", value: "input-1" }, @@ -10,10 +13,36 @@ export const STATIC_CONDITIONS = { { type: "number.range", label: "between", value: "input-4" }, ], collection: [{ type: "multiselect", label: "in", value: "input-4" }], - producttype: [{ type: "multiselect", label: "in", value: "input-4" }], channel: [{ type: "select", label: "is", value: "input-5" }], + productType: [ + { type: "combobox", label: "is", value: "input-1" }, + { type: "multiselect", label: "in", value: "input-2" } + ], + isAvailable: [{ type: "select", label: "is", value: "input-1" }], + isPublished: [{ type: "select", label: "is", value: "input-1" }], + isVisibleInListing: [{ type: "select", label: "is", value: "input-1" }], + hasCategory: [{ type: "select", label: "is", value: "input-1" }], + giftCard: [{ type: "select", label: "is", value: "input-1" }], }; +export const STATIC_OPTIONS: LeftOperand[] = [ + { value: "price", label: "Price", type: "price", slug: "price" }, + { value: "category", label: "Category", type: "category", slug: "category" }, + { + value: "collection", + label: "Collection", + type: "collection", + slug: "collection", + }, + { value: "channel", label: "Channel", type: "channel", slug: "channel" }, + { value: "productType", label: "Product Type", type: "productType", slug: "productType" }, + { value: "isAvailable", label: "Is available", type: "isAvailable", slug: "isAvailable" }, + { value: "isPublished", label: "Is published", type: "isPublished", slug: "isPublished" }, + { value: "isVisibleInListing", label: "Visible in listing", type: "isVisibleInListing", slug: "isVisibleInListing" }, + { value: "hasCategory", label: "Has category", type: "hasCategory", slug: "hasCategory" }, + { value: "giftCard", label: "Has giftcard", type: "giftCard", slug: "giftCard" }, +]; + export const ATTRIBUTE_INPUT_TYPE_CONDITIONS = { DROPDOWN: [{ type: "multiselect", label: "in", value: "input-2" }], MULTISELECT: [{ type: "multiselect", label: "in", value: "input-2" }], @@ -28,3 +57,19 @@ export const ATTRIBUTE_INPUT_TYPE_CONDITIONS = { DATE: [{ type: "date", label: "is", value: "input-1" }], SWATCH: [{ type: "multiselect", label: "in", value: "input-2" }], }; + + +export type RowType = keyof typeof STATIC_CONDITIONS | "attribute" + +export const createBooleanOptions = (): ItemOption[] => [ + { + label: "Yes", + value: "true", + slug: "true" + }, + { + label: "No", + value: "false", + slug: "false" + } +] \ No newline at end of file diff --git a/src/components/ConditionalFilter/useFilterLeftOperands.ts b/src/components/ConditionalFilter/useFilterLeftOperands.ts index 63340cf65..cd992c6a8 100644 --- a/src/components/ConditionalFilter/useFilterLeftOperands.ts +++ b/src/components/ConditionalFilter/useFilterLeftOperands.ts @@ -1,18 +1,8 @@ import { useState } from "react"; +import { STATIC_OPTIONS } from "./constants"; import { LeftOperand, LeftOperandsProvider } from "./LeftOperandsProvider"; -const STATIC_OPTIONS: LeftOperand[] = [ - { value: "price", label: "Price", type: "price", slug: "price" }, - { value: "category", label: "Category", type: "category", slug: "category" }, - { - value: "collection", - label: "Collection", - type: "collection", - slug: "collection", - }, - { value: "channel", label: "Channel", type: "channel", slug: "channel" }, -]; export const useFilterLeftOperandsProvider = (): LeftOperandsProvider => { const [operands, setOperands] = useState(STATIC_OPTIONS);