From 3bdd0dab6fab16d8666a8f5cfbabb230e05be26d Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Thu, 16 Jan 2020 14:49:06 +0100 Subject: [PATCH] Add product type filter --- .../components/ProductListPage/filters.ts | 20 +++++ src/products/queries.ts | 18 +++- .../types/InitialProductFilterData.ts | 18 ++++ src/products/urls.ts | 3 +- .../views/ProductList/ProductList.tsx | 33 ++++++- src/products/views/ProductList/filters.ts | 88 +++++++++++++++---- 6 files changed, 154 insertions(+), 26 deletions(-) diff --git a/src/products/components/ProductListPage/filters.ts b/src/products/components/ProductListPage/filters.ts index 70205b500..1ac4bd5f7 100644 --- a/src/products/components/ProductListPage/filters.ts +++ b/src/products/components/ProductListPage/filters.ts @@ -15,6 +15,7 @@ export enum ProductFilterKeys { collections = "collections", status = "status", price = "price", + productType = "productType", stock = "stock" } @@ -22,6 +23,7 @@ export interface ProductListFilterOpts { categories: FilterOpts & AutocompleteFilterOpts; collections: FilterOpts & AutocompleteFilterOpts; price: FilterOpts; + productType: FilterOpts & AutocompleteFilterOpts; status: FilterOpts; stockStatus: FilterOpts; } @@ -147,6 +149,24 @@ export function createFilterStructure( } ), active: opts.collections.active + }, + { + ...createAutocompleteField( + ProductFilterKeys.productType, + intl.formatMessage(sectionNames.productTypes), + opts.productType.value, + opts.productType.displayValues, + true, + opts.productType.choices, + { + hasMore: opts.productType.hasMore, + initialSearch: "", + loading: opts.productType.loading, + onFetchMore: opts.productType.onFetchMore, + onSearchChange: opts.productType.onSearchChange + } + ), + active: opts.productType.active } ]; } diff --git a/src/products/queries.ts b/src/products/queries.ts index 513ec26b0..ca5c1a273 100644 --- a/src/products/queries.ts +++ b/src/products/queries.ts @@ -215,8 +215,12 @@ export const fragmentVariant = gql` `; const initialProductFilterDataQuery = gql` - query InitialProductFilterData($categories: [ID!], $collections: [ID!]) { - categories(first: 20, filter: { ids: $categories }) { + query InitialProductFilterData( + $categories: [ID!] + $collections: [ID!] + $productTypes: [ID!] + ) { + categories(first: 100, filter: { ids: $categories }) { edges { node { id @@ -224,7 +228,15 @@ const initialProductFilterDataQuery = gql` } } } - collections(first: 20, filter: { ids: $collections }) { + collections(first: 100, filter: { ids: $collections }) { + edges { + node { + id + name + } + } + } + productTypes(first: 100, filter: { ids: $productTypes }) { edges { node { id diff --git a/src/products/types/InitialProductFilterData.ts b/src/products/types/InitialProductFilterData.ts index 58d6b9e29..c4c323c0d 100644 --- a/src/products/types/InitialProductFilterData.ts +++ b/src/products/types/InitialProductFilterData.ts @@ -38,12 +38,30 @@ export interface InitialProductFilterData_collections { edges: InitialProductFilterData_collections_edges[]; } +export interface InitialProductFilterData_productTypes_edges_node { + __typename: "ProductType"; + id: string; + name: string; +} + +export interface InitialProductFilterData_productTypes_edges { + __typename: "ProductTypeCountableEdge"; + node: InitialProductFilterData_productTypes_edges_node; +} + +export interface InitialProductFilterData_productTypes { + __typename: "ProductTypeCountableConnection"; + edges: InitialProductFilterData_productTypes_edges[]; +} + export interface InitialProductFilterData { categories: InitialProductFilterData_categories | null; collections: InitialProductFilterData_collections | null; + productTypes: InitialProductFilterData_productTypes | null; } export interface InitialProductFilterDataVariables { categories?: string[] | null; collections?: string[] | null; + productTypes?: string[] | null; } diff --git a/src/products/urls.ts b/src/products/urls.ts index ac5a946a9..0f56302ea 100644 --- a/src/products/urls.ts +++ b/src/products/urls.ts @@ -33,7 +33,8 @@ export enum ProductListUrlFiltersEnum { } export enum ProductListUrlFiltersWithMultipleValues { categories = "categories", - collections = "collections" + collections = "collections", + productTypes = "productTypes" } export type ProductListUrlFilters = Filters & FiltersWithMultipleValues; diff --git a/src/products/views/ProductList/ProductList.tsx b/src/products/views/ProductList/ProductList.tsx index a84ee4d30..5d735a38c 100644 --- a/src/products/views/ProductList/ProductList.tsx +++ b/src/products/views/ProductList/ProductList.tsx @@ -32,6 +32,7 @@ import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandl import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch"; +import useProductTypeSearch from "@saleor/searches/useProductTypeSearch"; import ProductListPage from "../../components/ProductListPage"; import { TypedProductBulkDeleteMutation, @@ -81,17 +82,34 @@ export const ProductList: React.FC = ({ params }) => { ); const intl = useIntl(); const { data: initialFilterData } = useInitialProductFilterDataQuery({ - skip: !(!!params.categories || !!params.collections), + skip: !( + !!params.categories || + !!params.collections || + !!params.productTypes + ), variables: { categories: params.categories, - collections: params.collections + collections: params.collections, + productTypes: params.productTypes } }); const searchCategories = useCategorySearch({ - variables: DEFAULT_INITIAL_SEARCH_DATA + variables: { + ...DEFAULT_INITIAL_SEARCH_DATA, + first: 5 + } }); const searchCollections = useCollectionSearch({ - variables: DEFAULT_INITIAL_SEARCH_DATA + variables: { + ...DEFAULT_INITIAL_SEARCH_DATA, + first: 5 + } + }); + const searchProductTypes = useProductTypeSearch({ + variables: { + ...DEFAULT_INITIAL_SEARCH_DATA, + first: 5 + } }); React.useEffect( @@ -191,6 +209,13 @@ export const ProductList: React.FC = ({ params }) => { [] ), search: searchCollections + }, + { + initial: maybe( + () => initialFilterData.productTypes.edges.map(edge => edge.node), + [] + ), + search: searchProductTypes } ); diff --git a/src/products/views/ProductList/filters.ts b/src/products/views/ProductList/filters.ts index 7860fe536..02d0f4725 100644 --- a/src/products/views/ProductList/filters.ts +++ b/src/products/views/ProductList/filters.ts @@ -11,12 +11,17 @@ import { } from "@saleor/searches/types/SearchCategories"; import { InitialProductFilterData_categories_edges_node, - InitialProductFilterData_collections_edges_node + InitialProductFilterData_collections_edges_node, + InitialProductFilterData_productTypes_edges_node } from "@saleor/products/types/InitialProductFilterData"; import { SearchCollections, SearchCollectionsVariables } from "@saleor/searches/types/SearchCollections"; +import { + SearchProductTypes, + SearchProductTypesVariables +} from "@saleor/searches/types/SearchProductTypes"; import { IFilterElement } from "../../../components/Filter"; import { ProductFilterInput, @@ -29,7 +34,8 @@ import { getMinMaxQueryParam, getSingleEnumValueQueryParam, dedupeFilter, - getMultipleValueQueryParam + getMultipleValueQueryParam, + getSingleValueQueryParam } from "../../../utils/filters"; import { ProductListUrlFilters, @@ -49,6 +55,10 @@ export function getFilterOpts( collections: { initial: InitialProductFilterData_collections_edges_node[]; search: UseSearchResult; + }, + productTypes: { + initial: InitialProductFilterData_productTypes_edges_node[]; + search: UseSearchResult; } ): ProductListFilterOpts { return { @@ -62,14 +72,16 @@ export function getFilterOpts( })), [] ), - displayValues: maybe( - () => - categories.initial.map(category => ({ - label: category.name, - value: category.id - })), - [] - ), + displayValues: !!params.categories + ? maybe( + () => + categories.initial.map(category => ({ + label: category.name, + value: category.id + })), + [] + ) + : [], hasMore: maybe( () => categories.search.result.data.search.pageInfo.hasNextPage, false @@ -90,14 +102,16 @@ export function getFilterOpts( })), [] ), - displayValues: maybe( - () => - collections.initial.map(category => ({ - label: category.name, - value: category.id - })), - [] - ), + displayValues: !!params.collections + ? maybe( + () => + collections.initial.map(category => ({ + label: category.name, + value: category.id + })), + [] + ) + : undefined, hasMore: maybe( () => collections.search.result.data.search.pageInfo.hasNextPage, false @@ -119,6 +133,36 @@ export function getFilterOpts( min: maybe(() => params.priceFrom, "0") } }, + productType: { + active: !!params.productTypes, + choices: maybe( + () => + productTypes.search.result.data.search.edges.map(edge => ({ + label: edge.node.name, + value: edge.node.id + })), + [] + ), + displayValues: !!params.productTypes + ? maybe( + () => + productTypes.initial.map(productType => ({ + label: productType.name, + value: productType.id + })), + [] + ) + : [], + hasMore: maybe( + () => productTypes.search.result.data.search.pageInfo.hasNextPage, + false + ), + initialSearch: "", + loading: productTypes.search.result.loading, + onFetchMore: productTypes.search.loadMore, + onSearchChange: productTypes.search.search, + value: maybe(() => dedupeFilter(params.productTypes), []) + }, status: { active: maybe(() => params.status !== undefined, false), value: maybe(() => findValueInEnum(params.status, ProductStatus)) @@ -144,6 +188,8 @@ export function getFilterVariables( gte: parseFloat(params.priceFrom), lte: parseFloat(params.priceTo) }), + productType: + params.productTypes !== undefined ? params.productTypes[0] : null, search: params.query, stockAvailability: params.stockStatus !== undefined @@ -177,6 +223,12 @@ export function getFilterQueryParam( ProductListUrlFiltersEnum.priceTo ); + case ProductFilterKeys.productType: + return getMultipleValueQueryParam( + filter, + ProductListUrlFiltersWithMultipleValues.productTypes + ); + case ProductFilterKeys.status: return getSingleEnumValueQueryParam( filter,