Experimental filters: Add API types and handlers (#3841)

This commit is contained in:
Krzysztof Żuraw 2023-07-05 09:21:32 +02:00 committed by GitHub
parent 8d425700eb
commit fec476b7e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 236 additions and 191 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-dashboard": minor
---
Add API handlers to expermiental filters

View 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 ?? "",
})) ?? []
);
};
}

View file

@ -1,16 +1,16 @@
// @ts-strict-ignore
import { ApolloClient } from "@apollo/client";
import {
_GetAttributeChoicesDocument,
_GetCategoriesChoicesDocument,
_GetChannelOperandsDocument,
_GetCollectionsChoicesDocument,
_GetDynamicLeftOperandsDocument,
_GetProductTypesChoicesDocument,
} from "@dashboard/graphql";
import { FilterElement } from "../FilterElement";
import { FilterContainer } from "../useFilterContainer";
import {
AttributeChoicesHandler,
AttributesHandler,
CategoryHandler,
ChannelHandler,
CollectionHandler,
Handler,
ProductTypeHandler,
} from "./Handler";
const getFilterElement = (value: any, index: number): FilterElement => {
const possibleFilterElement = value[index];
@ -19,193 +19,67 @@ const getFilterElement = (value: any, index: number): FilterElement => {
: 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 (
client: ApolloClient<any>,
client: ApolloClient<unknown>,
position: string,
value: FilterContainer,
) => {
const index = parseInt(position, 10);
const filterElement = getFilterElement(value, index);
const handler = createAPIHandler(filterElement, client, "");
if (filterElement.isAttribute()) {
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,
}));
}
return handler.fetch();
};
export const getRightOperatorOptionsByQuery = async (
client: ApolloClient<any>,
client: ApolloClient<unknown>,
position: string,
value: FilterContainer,
inputValue: string,
) => {
const index = parseInt(position, 10);
const filterElement = getFilterElement(value, index);
const handler = createAPIHandler(filterElement, client, inputValue);
if (filterElement.isAttribute()) {
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()),
);
}
return handler.fetch();
};
export const getLeftOperatorOptions = async (
client: any,
client: ApolloClient<unknown>,
inputValue: string,
) => {
const { data } = await client.query({
query: _GetDynamicLeftOperandsDocument,
variables: {
first: 5,
query: inputValue,
},
});
return data.attributes.edges.map(({ node }) => ({
label: node.name,
value: node.id,
type: node.inputType,
slug: node.slug,
}));
const handler = new AttributesHandler(client, inputValue);
return handler.fetch();
};

View file

@ -1,3 +1,5 @@
import { AttributeInputTypeEnum } from "@dashboard/graphql";
import {
ATTRIBUTE_INPUT_TYPE_CONDITIONS,
STATIC_CONDITIONS,
@ -50,7 +52,9 @@ export class ConditionOptions extends Array<ConditionItem> {
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 optionsAttribute =
this.isAttributeInputType(name) && ATTRIBUTE_INPUT_TYPE_CONDITIONS[name];

View file

@ -61,9 +61,5 @@ export class ConditionSelected {
public setOptions(options: ConditionValue[]) {
this.options = options;
if (this.conditionValue) {
this.value = getDefaultByControlName(this.conditionValue.type);
}
}
}

View file

@ -93,12 +93,12 @@ const FiltersArea = ({ provider, onConfirm }) => {
const handleLeftOperatorInputValueChange = (event: any) => {
const fetchAPI = async () => {
updateLeftLoadingState(event.path, true);
const options = await getLeftOperatorOptions(client, event.value);
setOperands(options);
updateLeftLoadingState(event.path, false);
setOperands(prev => [...prev, ...options]);
};
updateLeftLoadingState(event.path, true);
fetchAPI();
updateLeftLoadingState(event.path, false);
};
const handleLeftOperatorInputValueChangeDebounced = useDebounce(
@ -108,17 +108,17 @@ const FiltersArea = ({ provider, onConfirm }) => {
const handleRightOperatorInputValueChange = (event: any) => {
const fetchAPI = async () => {
updateRightLoadingState(event.path.split(".")[0], true);
const options = await getRightOperatorOptionsByQuery(
client,
event.path.split(".")[0],
value,
event.value,
);
updateRightLoadingState(event.path.split(".")[0], false);
updateRightOptions(event.path.split(".")[0], options);
};
updateRightLoadingState(event.path.split(".")[0], true);
fetchAPI();
updateRightLoadingState(event.path.split(".")[0], false);
};
const handleRightOperatorInputValueChangeDebounced = useDebounce(

View file

@ -1,12 +1,10 @@
import { AttributeInputTypeEnum } from "@dashboard/graphql";
import { useState } from "react";
import {
AttributeInputType,
StaticElementName,
} from "./FilterElement/ConditionOptions";
import { StaticElementName } from "./FilterElement/ConditionOptions";
export interface LeftOperand {
type: AttributeInputType | StaticElementName;
type: AttributeInputTypeEnum | StaticElementName;
label: string;
value: string;
slug: string;