Add all static fields to be used in filtering products (#3912)

* Add all static fields

* Changeset

* Add all static fields

* Fix types

* Remove unused entries
This commit is contained in:
Patryk Andrzejewski 2023-07-14 15:27:44 +02:00 committed by GitHub
parent 423858e86f
commit a7d39d7f19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 173 additions and 53 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---
Add all static fields for product filtering

View file

@ -24,8 +24,6 @@ import { ItemOption } from "../FilterElement/ConditionValue";
import { LeftOperand } from "../LeftOperandsProvider"; import { LeftOperand } from "../LeftOperandsProvider";
export interface Handler { export interface Handler {
client: ApolloClient<unknown>;
query: string;
fetch: () => Promise<ItemOption[]>; fetch: () => Promise<ItemOption[]>;
} }
@ -167,3 +165,11 @@ export class AttributesHandler implements Handler {
); );
}; };
} }
export class BooleanValuesHandler implements Handler {
constructor(public options: LeftOperand[]) {}
fetch = async (): Promise<LeftOperand[]> => {
return this.options
};
}

View file

@ -15,16 +15,26 @@ export interface InitialState {
attribute: Record<string, AttributeDTO>; attribute: Record<string, AttributeDTO>;
channel: ItemOption[]; channel: ItemOption[];
collection: ItemOption[]; collection: ItemOption[];
producttype: ItemOption[]; productType: ItemOption[];
isAvailable: ItemOption[];
isPublished: ItemOption[];
isVisibleInListing: ItemOption[];
hasCategory: ItemOption[];
giftCard: ItemOption[];
} }
export class InitialStateResponse implements InitialState { export class InitialStateResponse implements InitialState {
constructor( constructor(
public category: ItemOption[], public category: ItemOption[] = [],
public attribute: Record<string, AttributeDTO>, public attribute: Record<string, AttributeDTO> = {},
public channel: ItemOption[], public channel: ItemOption[] = [],
public collection: ItemOption[], public collection: ItemOption[] = [],
public producttype: ItemOption[], public productType: ItemOption[] = [],
public isAvailable: ItemOption[] = [],
public isPublished: ItemOption[] = [],
public isVisibleInListing: ItemOption[] = [],
public hasCategory: ItemOption[] = [],
public giftCard: ItemOption[] = []
) { } ) { }
public attributeByName(name: string) { public attributeByName(name: string) {
@ -32,7 +42,7 @@ export class InitialStateResponse implements InitialState {
} }
public static empty() { public static empty() {
return new InitialStateResponse([], {}, [], [], []); return new InitialStateResponse();
} }
public filterByUrlToken(token: UrlToken) { public filterByUrlToken(token: UrlToken) {
@ -57,10 +67,20 @@ export class InitialStateResponse implements InitialState {
return this.category; return this.category;
case "collection": case "collection":
return this.collection; return this.collection;
case "producttype": case "productType":
return this.producttype; return this.productType;
case "channel": case "channel":
return this.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: default:
return []; return [];
} }

View file

@ -1,10 +1,12 @@
import { ApolloClient, useApolloClient } from "@apollo/client"; import { ApolloClient, useApolloClient } from "@apollo/client";
import { RowType } from "../constants";
import { FilterContainer, FilterElement } from "../FilterElement"; import { FilterContainer, FilterElement } from "../FilterElement";
import { FilterAPIProvider } from "./FilterAPIProvider"; import { FilterAPIProvider } from "./FilterAPIProvider";
import { import {
AttributeChoicesHandler, AttributeChoicesHandler,
AttributesHandler, AttributesHandler,
BooleanValuesHandler,
CategoryHandler, CategoryHandler,
ChannelHandler, ChannelHandler,
CollectionHandler, CollectionHandler,
@ -27,12 +29,24 @@ const getFilterElement = (
throw new Error("Unknown filter element used to create API handler"); 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 = ( const createAPIHandler = (
selectedRow: FilterElement, selectedRow: FilterElement,
client: ApolloClient<unknown>, client: ApolloClient<unknown>,
inputValue: string, inputValue: string,
): Handler => { ): Handler => {
if (selectedRow.isAttribute()) { const rowType = selectedRow.rowType()
if (rowType === "attribute") {
return new AttributeChoicesHandler( return new AttributeChoicesHandler(
client, client,
selectedRow.value.value, 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); return new CollectionHandler(client, inputValue);
} }
if (selectedRow.isCategory()) { if (rowType === "category") {
return new CategoryHandler(client, inputValue); return new CategoryHandler(client, inputValue);
} }
if (selectedRow.isProductType()) { if (rowType === "productType") {
return new ProductTypeHandler(client, inputValue); return new ProductTypeHandler(client, inputValue);
} }
if (selectedRow.isChannel()) { if (rowType === "channel") {
return new ChannelHandler(client, inputValue); return new ChannelHandler(client, inputValue);
} }

View file

@ -7,6 +7,7 @@ import {
_SearchProductTypesOperandsQuery, _SearchProductTypesOperandsQuery,
} from "@dashboard/graphql"; } from "@dashboard/graphql";
import { createBooleanOptions } from "../../constants";
import { createOptionsFromAPI } from "../Handler"; import { createOptionsFromAPI } from "../Handler";
import { InitialState } from "../InitialStateResponse"; import { InitialState } from "../InitialStateResponse";
import { InitialAPIResponse } from "./types"; import { InitialAPIResponse } from "./types";
@ -70,7 +71,7 @@ export const createInitialStateFromData = (
if (isProductTypeQuery(query)) { if (isProductTypeQuery(query)) {
return { return {
...acc, ...acc,
producttype: createOptionsFromAPI( productType: createOptionsFromAPI(
query.data?.productTypes?.edges ?? [], query.data?.productTypes?.edges ?? [],
), ),
}; };
@ -102,7 +103,12 @@ export const createInitialStateFromData = (
channel: [], channel: [],
collection: [], collection: [],
category: [], category: [],
producttype: [], productType: [],
isAvailable: createBooleanOptions(),
isPublished: createBooleanOptions(),
isVisibleInListing: createBooleanOptions(),
hasCategory: createBooleanOptions(),
giftCard: createBooleanOptions(),
attribute: {}, attribute: {},
}, },
); );

View file

@ -40,7 +40,7 @@ export const useProductInitialAPIState = (): InitialAPIState => {
const fetchQueries = async ({ const fetchQueries = async ({
category, category,
collection, collection,
producttype, productType,
channel, channel,
attribute, attribute,
}: FetchingParams) => { }: FetchingParams) => {
@ -85,7 +85,7 @@ export const useProductInitialAPIState = (): InitialAPIState => {
); );
} }
if (producttype.length > 0) { if (productType.length > 0) {
queriesToRun.push( queriesToRun.push(
client.query< client.query<
_SearchProductTypesOperandsQuery, _SearchProductTypesOperandsQuery,
@ -93,8 +93,8 @@ export const useProductInitialAPIState = (): InitialAPIState => {
>({ >({
query: _SearchProductTypesOperandsDocument, query: _SearchProductTypesOperandsDocument,
variables: { variables: {
productTypesSlugs: producttype, productTypesSlugs: productType,
first: producttype.length, first: productType.length,
}, },
}), }),
); );
@ -124,7 +124,12 @@ export const useProductInitialAPIState = (): InitialAPIState => {
initialState.attribute, initialState.attribute,
initialState.channel, initialState.channel,
initialState.collection, initialState.collection,
initialState.producttype, initialState.productType,
initialState.isAvailable,
initialState.isPublished,
initialState.isVisibleInListing,
initialState.hasCategory,
initialState.giftCard
), ),
); );
setLoading(false); setLoading(false);

View file

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/member-ordering */ /* eslint-disable @typescript-eslint/member-ordering */
import { InitialStateResponse } from "../API/InitialStateResponse"; import { InitialStateResponse } from "../API/InitialStateResponse";
import { RowType, STATIC_OPTIONS } from "../constants";
import { LeftOperand } from "../LeftOperandsProvider"; import { LeftOperand } from "../LeftOperandsProvider";
import { TokenType, UrlEntry, UrlToken } from "./../ValueProvider/UrlToken"; import { TokenType, UrlEntry, UrlToken } from "./../ValueProvider/UrlToken";
import { Condition } from "./Condition"; import { Condition } from "./Condition";
@ -23,9 +24,15 @@ class ExpressionValue {
} }
public static fromUrlToken(token: UrlToken) { public static fromUrlToken(token: UrlToken) {
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, token.name, token.name);
} }
return new ExpressionValue(token.name, option.label, token.name);
}
public static forAttribute( public static forAttribute(
attributeName: string, attributeName: string,
response: InitialStateResponse, response: InitialStateResponse,
@ -106,23 +113,18 @@ export class FilterElement {
return ConditionOptions.isAttributeInputType(this.value.type); return ConditionOptions.isAttributeInputType(this.value.type);
} }
public isCollection() { public rowType (): RowType | null {
return this.value.value === "collection"; if (this.isStatic()) {
return this.value.value as RowType
} }
public isCategory() { if (this.isAttribute()) {
return this.value.value === "category"; return "attribute"
} }
public isProductType() { return null;
return this.value.value === "producttype";
} }
public isChannel() {
return this.value.value === "channel";
}
public asUrlEntry(): UrlEntry { public asUrlEntry(): UrlEntry {
if (this.isAttribute()) { if (this.isAttribute()) {
return UrlEntry.forAttribute(this.condition.selected, this.value.value); return UrlEntry.forAttribute(this.condition.selected, this.value.value);

View file

@ -4,7 +4,7 @@ export interface FetchingParams {
category: string[]; category: string[];
collection: string[]; collection: string[];
channel: string[]; channel: string[];
producttype: string[]; productType: string[];
attribute: Record<string, string[]>; attribute: Record<string, string[]>;
} }
@ -14,7 +14,7 @@ export const emptyFetchingParams: FetchingParams = {
category: [], category: [],
collection: [], collection: [],
channel: [], channel: [],
producttype: [], productType: [],
attribute: {}, attribute: {},
}; };

View file

@ -5,7 +5,17 @@ import { slugFromConditionValue } from "../FilterElement/ConditionValue";
export const CONDITIONS = ["is", "equals", "in", "between", "lower", "greater"]; 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 = { export const TokenType = {
ATTRIBUTE: "a", ATTRIBUTE: "a",

View file

@ -1,3 +1,6 @@
import { ItemOption } from "./FilterElement/ConditionValue";
import { LeftOperand } from "./LeftOperandsProvider";
export const STATIC_CONDITIONS = { export const STATIC_CONDITIONS = {
category: [ category: [
{ type: "combobox", label: "is", value: "input-1" }, { type: "combobox", label: "is", value: "input-1" },
@ -10,10 +13,36 @@ export const STATIC_CONDITIONS = {
{ type: "number.range", label: "between", value: "input-4" }, { type: "number.range", label: "between", value: "input-4" },
], ],
collection: [{ type: "multiselect", label: "in", 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" }], 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 = { export const ATTRIBUTE_INPUT_TYPE_CONDITIONS = {
DROPDOWN: [{ type: "multiselect", label: "in", value: "input-2" }], DROPDOWN: [{ type: "multiselect", label: "in", value: "input-2" }],
MULTISELECT: [{ 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" }], DATE: [{ type: "date", label: "is", value: "input-1" }],
SWATCH: [{ type: "multiselect", label: "in", value: "input-2" }], 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"
}
]

View file

@ -1,18 +1,8 @@
import { useState } from "react"; import { useState } from "react";
import { STATIC_OPTIONS } from "./constants";
import { LeftOperand, LeftOperandsProvider } from "./LeftOperandsProvider"; 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 => { export const useFilterLeftOperandsProvider = (): LeftOperandsProvider => {
const [operands, setOperands] = useState<LeftOperand[]>(STATIC_OPTIONS); const [operands, setOperands] = useState<LeftOperand[]>(STATIC_OPTIONS);