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:
parent
423858e86f
commit
a7d39d7f19
11 changed files with 173 additions and 53 deletions
5
.changeset/quiet-wolves-boil.md
Normal file
5
.changeset/quiet-wolves-boil.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-dashboard": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add all static fields for product filtering
|
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -15,24 +15,34 @@ 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) {
|
||||||
return this.attribute[name];
|
return this.attribute[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
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 [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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: {},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue