Add product type filter

This commit is contained in:
dominik-zeglen 2020-01-16 14:49:06 +01:00
parent 0a58c3a5e1
commit 3bdd0dab6f
6 changed files with 154 additions and 26 deletions

View file

@ -15,6 +15,7 @@ export enum ProductFilterKeys {
collections = "collections", collections = "collections",
status = "status", status = "status",
price = "price", price = "price",
productType = "productType",
stock = "stock" stock = "stock"
} }
@ -22,6 +23,7 @@ export interface ProductListFilterOpts {
categories: FilterOpts<string[]> & AutocompleteFilterOpts; categories: FilterOpts<string[]> & AutocompleteFilterOpts;
collections: FilterOpts<string[]> & AutocompleteFilterOpts; collections: FilterOpts<string[]> & AutocompleteFilterOpts;
price: FilterOpts<MinMax>; price: FilterOpts<MinMax>;
productType: FilterOpts<string[]> & AutocompleteFilterOpts;
status: FilterOpts<ProductStatus>; status: FilterOpts<ProductStatus>;
stockStatus: FilterOpts<StockAvailability>; stockStatus: FilterOpts<StockAvailability>;
} }
@ -147,6 +149,24 @@ export function createFilterStructure(
} }
), ),
active: opts.collections.active 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
} }
]; ];
} }

View file

@ -215,8 +215,12 @@ export const fragmentVariant = gql`
`; `;
const initialProductFilterDataQuery = gql` const initialProductFilterDataQuery = gql`
query InitialProductFilterData($categories: [ID!], $collections: [ID!]) { query InitialProductFilterData(
categories(first: 20, filter: { ids: $categories }) { $categories: [ID!]
$collections: [ID!]
$productTypes: [ID!]
) {
categories(first: 100, filter: { ids: $categories }) {
edges { edges {
node { node {
id 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 { edges {
node { node {
id id

View file

@ -38,12 +38,30 @@ export interface InitialProductFilterData_collections {
edges: InitialProductFilterData_collections_edges[]; 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 { export interface InitialProductFilterData {
categories: InitialProductFilterData_categories | null; categories: InitialProductFilterData_categories | null;
collections: InitialProductFilterData_collections | null; collections: InitialProductFilterData_collections | null;
productTypes: InitialProductFilterData_productTypes | null;
} }
export interface InitialProductFilterDataVariables { export interface InitialProductFilterDataVariables {
categories?: string[] | null; categories?: string[] | null;
collections?: string[] | null; collections?: string[] | null;
productTypes?: string[] | null;
} }

View file

@ -33,7 +33,8 @@ export enum ProductListUrlFiltersEnum {
} }
export enum ProductListUrlFiltersWithMultipleValues { export enum ProductListUrlFiltersWithMultipleValues {
categories = "categories", categories = "categories",
collections = "collections" collections = "collections",
productTypes = "productTypes"
} }
export type ProductListUrlFilters = Filters<ProductListUrlFiltersEnum> & export type ProductListUrlFilters = Filters<ProductListUrlFiltersEnum> &
FiltersWithMultipleValues<ProductListUrlFiltersWithMultipleValues>; FiltersWithMultipleValues<ProductListUrlFiltersWithMultipleValues>;

View file

@ -32,6 +32,7 @@ import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandl
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
import useCategorySearch from "@saleor/searches/useCategorySearch"; import useCategorySearch from "@saleor/searches/useCategorySearch";
import useCollectionSearch from "@saleor/searches/useCollectionSearch"; import useCollectionSearch from "@saleor/searches/useCollectionSearch";
import useProductTypeSearch from "@saleor/searches/useProductTypeSearch";
import ProductListPage from "../../components/ProductListPage"; import ProductListPage from "../../components/ProductListPage";
import { import {
TypedProductBulkDeleteMutation, TypedProductBulkDeleteMutation,
@ -81,17 +82,34 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
); );
const intl = useIntl(); const intl = useIntl();
const { data: initialFilterData } = useInitialProductFilterDataQuery({ const { data: initialFilterData } = useInitialProductFilterDataQuery({
skip: !(!!params.categories || !!params.collections), skip: !(
!!params.categories ||
!!params.collections ||
!!params.productTypes
),
variables: { variables: {
categories: params.categories, categories: params.categories,
collections: params.collections collections: params.collections,
productTypes: params.productTypes
} }
}); });
const searchCategories = useCategorySearch({ const searchCategories = useCategorySearch({
variables: DEFAULT_INITIAL_SEARCH_DATA variables: {
...DEFAULT_INITIAL_SEARCH_DATA,
first: 5
}
}); });
const searchCollections = useCollectionSearch({ 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( React.useEffect(
@ -191,6 +209,13 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
[] []
), ),
search: searchCollections search: searchCollections
},
{
initial: maybe(
() => initialFilterData.productTypes.edges.map(edge => edge.node),
[]
),
search: searchProductTypes
} }
); );

View file

@ -11,12 +11,17 @@ import {
} from "@saleor/searches/types/SearchCategories"; } from "@saleor/searches/types/SearchCategories";
import { import {
InitialProductFilterData_categories_edges_node, InitialProductFilterData_categories_edges_node,
InitialProductFilterData_collections_edges_node InitialProductFilterData_collections_edges_node,
InitialProductFilterData_productTypes_edges_node
} from "@saleor/products/types/InitialProductFilterData"; } from "@saleor/products/types/InitialProductFilterData";
import { import {
SearchCollections, SearchCollections,
SearchCollectionsVariables SearchCollectionsVariables
} from "@saleor/searches/types/SearchCollections"; } from "@saleor/searches/types/SearchCollections";
import {
SearchProductTypes,
SearchProductTypesVariables
} from "@saleor/searches/types/SearchProductTypes";
import { IFilterElement } from "../../../components/Filter"; import { IFilterElement } from "../../../components/Filter";
import { import {
ProductFilterInput, ProductFilterInput,
@ -29,7 +34,8 @@ import {
getMinMaxQueryParam, getMinMaxQueryParam,
getSingleEnumValueQueryParam, getSingleEnumValueQueryParam,
dedupeFilter, dedupeFilter,
getMultipleValueQueryParam getMultipleValueQueryParam,
getSingleValueQueryParam
} from "../../../utils/filters"; } from "../../../utils/filters";
import { import {
ProductListUrlFilters, ProductListUrlFilters,
@ -49,6 +55,10 @@ export function getFilterOpts(
collections: { collections: {
initial: InitialProductFilterData_collections_edges_node[]; initial: InitialProductFilterData_collections_edges_node[];
search: UseSearchResult<SearchCollections, SearchCollectionsVariables>; search: UseSearchResult<SearchCollections, SearchCollectionsVariables>;
},
productTypes: {
initial: InitialProductFilterData_productTypes_edges_node[];
search: UseSearchResult<SearchProductTypes, SearchProductTypesVariables>;
} }
): ProductListFilterOpts { ): ProductListFilterOpts {
return { return {
@ -62,14 +72,16 @@ export function getFilterOpts(
})), })),
[] []
), ),
displayValues: maybe( displayValues: !!params.categories
? maybe(
() => () =>
categories.initial.map(category => ({ categories.initial.map(category => ({
label: category.name, label: category.name,
value: category.id value: category.id
})), })),
[] []
), )
: [],
hasMore: maybe( hasMore: maybe(
() => categories.search.result.data.search.pageInfo.hasNextPage, () => categories.search.result.data.search.pageInfo.hasNextPage,
false false
@ -90,14 +102,16 @@ export function getFilterOpts(
})), })),
[] []
), ),
displayValues: maybe( displayValues: !!params.collections
? maybe(
() => () =>
collections.initial.map(category => ({ collections.initial.map(category => ({
label: category.name, label: category.name,
value: category.id value: category.id
})), })),
[] []
), )
: undefined,
hasMore: maybe( hasMore: maybe(
() => collections.search.result.data.search.pageInfo.hasNextPage, () => collections.search.result.data.search.pageInfo.hasNextPage,
false false
@ -119,6 +133,36 @@ export function getFilterOpts(
min: maybe(() => params.priceFrom, "0") 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: { status: {
active: maybe(() => params.status !== undefined, false), active: maybe(() => params.status !== undefined, false),
value: maybe(() => findValueInEnum(params.status, ProductStatus)) value: maybe(() => findValueInEnum(params.status, ProductStatus))
@ -144,6 +188,8 @@ export function getFilterVariables(
gte: parseFloat(params.priceFrom), gte: parseFloat(params.priceFrom),
lte: parseFloat(params.priceTo) lte: parseFloat(params.priceTo)
}), }),
productType:
params.productTypes !== undefined ? params.productTypes[0] : null,
search: params.query, search: params.query,
stockAvailability: stockAvailability:
params.stockStatus !== undefined params.stockStatus !== undefined
@ -177,6 +223,12 @@ export function getFilterQueryParam(
ProductListUrlFiltersEnum.priceTo ProductListUrlFiltersEnum.priceTo
); );
case ProductFilterKeys.productType:
return getMultipleValueQueryParam(
filter,
ProductListUrlFiltersWithMultipleValues.productTypes
);
case ProductFilterKeys.status: case ProductFilterKeys.status:
return getSingleEnumValueQueryParam( return getSingleEnumValueQueryParam(
filter, filter,