Experimental filters: Add API types and handlers (#3841)
This commit is contained in:
parent
8d425700eb
commit
fec476b7e2
7 changed files with 236 additions and 191 deletions
5
.changeset/silent-hornets-sell.md
Normal file
5
.changeset/silent-hornets-sell.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-dashboard": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add API handlers to expermiental filters
|
168
src/components/ConditionalFilter/API/Handler.ts
Normal file
168
src/components/ConditionalFilter/API/Handler.ts
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
import { ApolloClient } from "@apollo/client";
|
||||||
|
import {
|
||||||
|
_GetAttributeChoicesDocument,
|
||||||
|
_GetAttributeChoicesQuery,
|
||||||
|
_GetAttributeChoicesQueryVariables,
|
||||||
|
_GetCategoriesChoicesDocument,
|
||||||
|
_GetCategoriesChoicesQuery,
|
||||||
|
_GetCategoriesChoicesQueryVariables,
|
||||||
|
_GetChannelOperandsDocument,
|
||||||
|
_GetChannelOperandsQuery,
|
||||||
|
_GetChannelOperandsQueryVariables,
|
||||||
|
_GetCollectionsChoicesDocument,
|
||||||
|
_GetCollectionsChoicesQuery,
|
||||||
|
_GetCollectionsChoicesQueryVariables,
|
||||||
|
_GetDynamicLeftOperandsDocument,
|
||||||
|
_GetDynamicLeftOperandsQuery,
|
||||||
|
_GetDynamicLeftOperandsQueryVariables,
|
||||||
|
_GetProductTypesChoicesDocument,
|
||||||
|
_GetProductTypesChoicesQuery,
|
||||||
|
_GetProductTypesChoicesQueryVariables,
|
||||||
|
} from "@dashboard/graphql";
|
||||||
|
|
||||||
|
import { ItemOption } from "../FilterElement/ConditionSelected";
|
||||||
|
|
||||||
|
export interface Handler {
|
||||||
|
client: ApolloClient<unknown>;
|
||||||
|
query: string;
|
||||||
|
fetch: () => Promise<ItemOption[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createOptionsFromAPI = (
|
||||||
|
// TODO: try to use type from graphql
|
||||||
|
data: Array<{ node: { name: string | null; id: string; slug: string } }>,
|
||||||
|
): ItemOption[] =>
|
||||||
|
data.map(({ node }) => ({
|
||||||
|
label: node.name ?? "",
|
||||||
|
value: node.id,
|
||||||
|
slug: node.slug,
|
||||||
|
}));
|
||||||
|
|
||||||
|
export class AttributeChoicesHandler implements Handler {
|
||||||
|
constructor(
|
||||||
|
public client: ApolloClient<unknown>,
|
||||||
|
public attributeSlug: string,
|
||||||
|
public query: string,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
fetch = async () => {
|
||||||
|
const { client, attributeSlug, query } = this;
|
||||||
|
const { data } = await client.query<
|
||||||
|
_GetAttributeChoicesQuery,
|
||||||
|
_GetAttributeChoicesQueryVariables
|
||||||
|
>({
|
||||||
|
query: _GetAttributeChoicesDocument,
|
||||||
|
variables: {
|
||||||
|
slug: attributeSlug,
|
||||||
|
first: 5,
|
||||||
|
query,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return createOptionsFromAPI(data.attribute?.choices?.edges ?? []);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CollectionHandler implements Handler {
|
||||||
|
constructor(public client: ApolloClient<unknown>, public query: string) {}
|
||||||
|
|
||||||
|
fetch = async () => {
|
||||||
|
const { data } = await this.client.query<
|
||||||
|
_GetCollectionsChoicesQuery,
|
||||||
|
_GetCollectionsChoicesQueryVariables
|
||||||
|
>({
|
||||||
|
query: _GetCollectionsChoicesDocument,
|
||||||
|
variables: {
|
||||||
|
first: 5,
|
||||||
|
query: this.query,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return createOptionsFromAPI(data.collections?.edges ?? []);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CategoryHandler implements Handler {
|
||||||
|
constructor(public client: ApolloClient<unknown>, public query: string) {}
|
||||||
|
|
||||||
|
fetch = async () => {
|
||||||
|
const { data } = await this.client.query<
|
||||||
|
_GetCategoriesChoicesQuery,
|
||||||
|
_GetCategoriesChoicesQueryVariables
|
||||||
|
>({
|
||||||
|
query: _GetCategoriesChoicesDocument,
|
||||||
|
variables: {
|
||||||
|
first: 5,
|
||||||
|
query: this.query,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return createOptionsFromAPI(data.categories?.edges ?? []);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProductTypeHandler implements Handler {
|
||||||
|
constructor(public client: ApolloClient<unknown>, public query: string) {}
|
||||||
|
|
||||||
|
fetch = async () => {
|
||||||
|
const { data } = await this.client.query<
|
||||||
|
_GetProductTypesChoicesQuery,
|
||||||
|
_GetProductTypesChoicesQueryVariables
|
||||||
|
>({
|
||||||
|
query: _GetProductTypesChoicesDocument,
|
||||||
|
variables: {
|
||||||
|
first: 5,
|
||||||
|
query: this.query,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return createOptionsFromAPI(data.productTypes?.edges ?? []);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChannelHandler implements Handler {
|
||||||
|
constructor(public client: ApolloClient<unknown>, public query: string) {}
|
||||||
|
|
||||||
|
fetch = async () => {
|
||||||
|
const { data } = await this.client.query<
|
||||||
|
_GetChannelOperandsQuery,
|
||||||
|
_GetChannelOperandsQueryVariables
|
||||||
|
>({
|
||||||
|
query: _GetChannelOperandsDocument,
|
||||||
|
});
|
||||||
|
const options =
|
||||||
|
data.channels?.map(({ id, name, slug }) => ({
|
||||||
|
label: name,
|
||||||
|
value: id,
|
||||||
|
slug,
|
||||||
|
})) ?? [];
|
||||||
|
|
||||||
|
return options.filter(({ label }) =>
|
||||||
|
label.toLowerCase().includes(this.query.toLowerCase()),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AttributesHandler implements Handler {
|
||||||
|
constructor(public client: ApolloClient<unknown>, public query: string) {}
|
||||||
|
|
||||||
|
fetch = async () => {
|
||||||
|
const { data } = await this.client.query<
|
||||||
|
_GetDynamicLeftOperandsQuery,
|
||||||
|
_GetDynamicLeftOperandsQueryVariables
|
||||||
|
>({
|
||||||
|
query: _GetDynamicLeftOperandsDocument,
|
||||||
|
variables: {
|
||||||
|
first: 5,
|
||||||
|
query: this.query,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
data.attributes?.edges.map(({ node }) => ({
|
||||||
|
label: node.name ?? "",
|
||||||
|
value: node.id,
|
||||||
|
type: node.inputType,
|
||||||
|
slug: node.slug ?? "",
|
||||||
|
})) ?? []
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
// @ts-strict-ignore
|
|
||||||
import { ApolloClient } from "@apollo/client";
|
import { ApolloClient } from "@apollo/client";
|
||||||
import {
|
|
||||||
_GetAttributeChoicesDocument,
|
|
||||||
_GetCategoriesChoicesDocument,
|
|
||||||
_GetChannelOperandsDocument,
|
|
||||||
_GetCollectionsChoicesDocument,
|
|
||||||
_GetDynamicLeftOperandsDocument,
|
|
||||||
_GetProductTypesChoicesDocument,
|
|
||||||
} from "@dashboard/graphql";
|
|
||||||
|
|
||||||
import { FilterElement } from "../FilterElement";
|
import { FilterElement } from "../FilterElement";
|
||||||
import { FilterContainer } from "../useFilterContainer";
|
import { FilterContainer } from "../useFilterContainer";
|
||||||
|
import {
|
||||||
|
AttributeChoicesHandler,
|
||||||
|
AttributesHandler,
|
||||||
|
CategoryHandler,
|
||||||
|
ChannelHandler,
|
||||||
|
CollectionHandler,
|
||||||
|
Handler,
|
||||||
|
ProductTypeHandler,
|
||||||
|
} from "./Handler";
|
||||||
|
|
||||||
const getFilterElement = (value: any, index: number): FilterElement => {
|
const getFilterElement = (value: any, index: number): FilterElement => {
|
||||||
const possibleFilterElement = value[index];
|
const possibleFilterElement = value[index];
|
||||||
|
@ -19,193 +19,67 @@ const getFilterElement = (value: any, index: number): FilterElement => {
|
||||||
: null;
|
: null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createAPIHandler = (
|
||||||
|
selectedRow: FilterElement,
|
||||||
|
client: ApolloClient<unknown>,
|
||||||
|
inputValue: string,
|
||||||
|
): Handler => {
|
||||||
|
if (selectedRow.isAttribute()) {
|
||||||
|
return new AttributeChoicesHandler(
|
||||||
|
client,
|
||||||
|
selectedRow.value.value,
|
||||||
|
inputValue,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedRow.isCollection()) {
|
||||||
|
return new CollectionHandler(client, inputValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedRow.isCategory()) {
|
||||||
|
return new CategoryHandler(client, inputValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedRow.isProductType()) {
|
||||||
|
return new ProductTypeHandler(client, inputValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedRow.isChannel()) {
|
||||||
|
return new ChannelHandler(client, inputValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Unknown filter element");
|
||||||
|
};
|
||||||
|
|
||||||
export const getInitialRightOperatorOptions = async (
|
export const getInitialRightOperatorOptions = async (
|
||||||
client: ApolloClient<any>,
|
client: ApolloClient<unknown>,
|
||||||
position: string,
|
position: string,
|
||||||
value: FilterContainer,
|
value: FilterContainer,
|
||||||
) => {
|
) => {
|
||||||
const index = parseInt(position, 10);
|
const index = parseInt(position, 10);
|
||||||
const filterElement = getFilterElement(value, index);
|
const filterElement = getFilterElement(value, index);
|
||||||
|
const handler = createAPIHandler(filterElement, client, "");
|
||||||
|
|
||||||
if (filterElement.isAttribute()) {
|
return handler.fetch();
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetAttributeChoicesDocument,
|
|
||||||
variables: {
|
|
||||||
slug: filterElement.value.value,
|
|
||||||
first: 5,
|
|
||||||
query: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return data.attribute.choices.edges.map(({ node }) => ({
|
|
||||||
label: node.name,
|
|
||||||
value: node.id,
|
|
||||||
slug: node.slug,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterElement.isCollection()) {
|
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetCollectionsChoicesDocument,
|
|
||||||
variables: {
|
|
||||||
first: 5,
|
|
||||||
query: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return data.collections.edges.map(({ node }) => ({
|
|
||||||
label: node.name,
|
|
||||||
value: node.id,
|
|
||||||
slug: node.slug,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterElement.isCategory()) {
|
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetCategoriesChoicesDocument,
|
|
||||||
variables: {
|
|
||||||
first: 5,
|
|
||||||
query: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return data.categories.edges.map(({ node }) => ({
|
|
||||||
label: node.name,
|
|
||||||
value: node.id,
|
|
||||||
slug: node.slug,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterElement.isProductType()) {
|
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetProductTypesChoicesDocument,
|
|
||||||
variables: {
|
|
||||||
first: 5,
|
|
||||||
query: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return data.productTypes.edges.map(({ node }) => ({
|
|
||||||
label: node.name,
|
|
||||||
value: node.id,
|
|
||||||
slug: node.slug,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterElement.isChannel()) {
|
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetChannelOperandsDocument,
|
|
||||||
});
|
|
||||||
|
|
||||||
return data.channels.map(({ id, name, slug }) => ({
|
|
||||||
label: name,
|
|
||||||
value: id,
|
|
||||||
slug,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRightOperatorOptionsByQuery = async (
|
export const getRightOperatorOptionsByQuery = async (
|
||||||
client: ApolloClient<any>,
|
client: ApolloClient<unknown>,
|
||||||
position: string,
|
position: string,
|
||||||
value: FilterContainer,
|
value: FilterContainer,
|
||||||
inputValue: string,
|
inputValue: string,
|
||||||
) => {
|
) => {
|
||||||
const index = parseInt(position, 10);
|
const index = parseInt(position, 10);
|
||||||
const filterElement = getFilterElement(value, index);
|
const filterElement = getFilterElement(value, index);
|
||||||
|
const handler = createAPIHandler(filterElement, client, inputValue);
|
||||||
|
|
||||||
if (filterElement.isAttribute()) {
|
return handler.fetch();
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetAttributeChoicesDocument,
|
|
||||||
variables: {
|
|
||||||
slug: filterElement.value.value,
|
|
||||||
first: 5,
|
|
||||||
query: inputValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return data.attribute.choices.edges.map(({ node }) => ({
|
|
||||||
label: node.name,
|
|
||||||
value: node.id,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterElement.isCollection()) {
|
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetCollectionsChoicesDocument,
|
|
||||||
variables: {
|
|
||||||
first: 5,
|
|
||||||
query: inputValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return data.collections.edges.map(({ node }) => ({
|
|
||||||
label: node.name,
|
|
||||||
value: node.id,
|
|
||||||
slug: node.slug,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterElement.isCategory()) {
|
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetCategoriesChoicesDocument,
|
|
||||||
variables: {
|
|
||||||
first: 5,
|
|
||||||
query: inputValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return data.categories.edges.map(({ node }) => ({
|
|
||||||
label: node.name,
|
|
||||||
value: node.id,
|
|
||||||
slug: node.slug,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterElement.isProductType()) {
|
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetProductTypesChoicesDocument,
|
|
||||||
variables: {
|
|
||||||
first: 5,
|
|
||||||
query: inputValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return data.productTypes.edges.map(({ node }) => ({
|
|
||||||
label: node.name,
|
|
||||||
value: node.id,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterElement.isChannel()) {
|
|
||||||
const { data } = await client.query({
|
|
||||||
query: _GetChannelOperandsDocument,
|
|
||||||
});
|
|
||||||
const options = data.channels.map(({ id, name, slug }) => ({
|
|
||||||
label: name,
|
|
||||||
value: id,
|
|
||||||
slug,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return options.filter(({ label }) =>
|
|
||||||
label.toLowerCase().includes(inputValue.toLowerCase()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLeftOperatorOptions = async (
|
export const getLeftOperatorOptions = async (
|
||||||
client: any,
|
client: ApolloClient<unknown>,
|
||||||
inputValue: string,
|
inputValue: string,
|
||||||
) => {
|
) => {
|
||||||
const { data } = await client.query({
|
const handler = new AttributesHandler(client, inputValue);
|
||||||
query: _GetDynamicLeftOperandsDocument,
|
return handler.fetch();
|
||||||
variables: {
|
|
||||||
first: 5,
|
|
||||||
query: inputValue,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return data.attributes.edges.map(({ node }) => ({
|
|
||||||
label: node.name,
|
|
||||||
value: node.id,
|
|
||||||
type: node.inputType,
|
|
||||||
slug: node.slug,
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { AttributeInputTypeEnum } from "@dashboard/graphql";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ATTRIBUTE_INPUT_TYPE_CONDITIONS,
|
ATTRIBUTE_INPUT_TYPE_CONDITIONS,
|
||||||
STATIC_CONDITIONS,
|
STATIC_CONDITIONS,
|
||||||
|
@ -50,7 +52,9 @@ export class ConditionOptions extends Array<ConditionItem> {
|
||||||
return new ConditionOptions(options);
|
return new ConditionOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fromName(name: AttributeInputType | StaticElementName) {
|
public static fromName(
|
||||||
|
name: AttributeInputType | StaticElementName | AttributeInputTypeEnum,
|
||||||
|
) {
|
||||||
const optionsStatic = this.isStaticName(name) && STATIC_CONDITIONS[name];
|
const optionsStatic = this.isStaticName(name) && STATIC_CONDITIONS[name];
|
||||||
const optionsAttribute =
|
const optionsAttribute =
|
||||||
this.isAttributeInputType(name) && ATTRIBUTE_INPUT_TYPE_CONDITIONS[name];
|
this.isAttributeInputType(name) && ATTRIBUTE_INPUT_TYPE_CONDITIONS[name];
|
||||||
|
|
|
@ -61,9 +61,5 @@ export class ConditionSelected {
|
||||||
|
|
||||||
public setOptions(options: ConditionValue[]) {
|
public setOptions(options: ConditionValue[]) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
if (this.conditionValue) {
|
|
||||||
this.value = getDefaultByControlName(this.conditionValue.type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,12 +93,12 @@ const FiltersArea = ({ provider, onConfirm }) => {
|
||||||
|
|
||||||
const handleLeftOperatorInputValueChange = (event: any) => {
|
const handleLeftOperatorInputValueChange = (event: any) => {
|
||||||
const fetchAPI = async () => {
|
const fetchAPI = async () => {
|
||||||
const options = await getLeftOperatorOptions(client, event.value);
|
|
||||||
setOperands(options);
|
|
||||||
};
|
|
||||||
updateLeftLoadingState(event.path, true);
|
updateLeftLoadingState(event.path, true);
|
||||||
fetchAPI();
|
const options = await getLeftOperatorOptions(client, event.value);
|
||||||
updateLeftLoadingState(event.path, false);
|
updateLeftLoadingState(event.path, false);
|
||||||
|
setOperands(prev => [...prev, ...options]);
|
||||||
|
};
|
||||||
|
fetchAPI();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLeftOperatorInputValueChangeDebounced = useDebounce(
|
const handleLeftOperatorInputValueChangeDebounced = useDebounce(
|
||||||
|
@ -108,17 +108,17 @@ const FiltersArea = ({ provider, onConfirm }) => {
|
||||||
|
|
||||||
const handleRightOperatorInputValueChange = (event: any) => {
|
const handleRightOperatorInputValueChange = (event: any) => {
|
||||||
const fetchAPI = async () => {
|
const fetchAPI = async () => {
|
||||||
|
updateRightLoadingState(event.path.split(".")[0], true);
|
||||||
const options = await getRightOperatorOptionsByQuery(
|
const options = await getRightOperatorOptionsByQuery(
|
||||||
client,
|
client,
|
||||||
event.path.split(".")[0],
|
event.path.split(".")[0],
|
||||||
value,
|
value,
|
||||||
event.value,
|
event.value,
|
||||||
);
|
);
|
||||||
|
updateRightLoadingState(event.path.split(".")[0], false);
|
||||||
updateRightOptions(event.path.split(".")[0], options);
|
updateRightOptions(event.path.split(".")[0], options);
|
||||||
};
|
};
|
||||||
updateRightLoadingState(event.path.split(".")[0], true);
|
|
||||||
fetchAPI();
|
fetchAPI();
|
||||||
updateRightLoadingState(event.path.split(".")[0], false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRightOperatorInputValueChangeDebounced = useDebounce(
|
const handleRightOperatorInputValueChangeDebounced = useDebounce(
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
|
import { AttributeInputTypeEnum } from "@dashboard/graphql";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import {
|
import { StaticElementName } from "./FilterElement/ConditionOptions";
|
||||||
AttributeInputType,
|
|
||||||
StaticElementName,
|
|
||||||
} from "./FilterElement/ConditionOptions";
|
|
||||||
|
|
||||||
export interface LeftOperand {
|
export interface LeftOperand {
|
||||||
type: AttributeInputType | StaticElementName;
|
type: AttributeInputTypeEnum | StaticElementName;
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
|
Loading…
Reference in a new issue