New filters for the product listing page (prototype) (#3811)
* Expression filters * Filters * Tokenizing * Tokenizing * feat: fetch inital state from API * fix: integrate with code * Loading * feat: add attribute name & label * feat: move input type * Loading * feat: update left operator + condition * feat: fetch inital options on focus * feat: fetch right options on autocomplete * Flags * Refactor * fix: add loading state * fix: after changes * fix: remove debugger * fix: proper selected setting * Refactor * Display properly * Display properly * Display properly * feat: fetch left options * Persist * feat: add loading state * feat: refactor getAPIOptions * feat: add additional checks to filter element * feat: use debounce * FilterArray * FilterArray * Modeling * fix: filters in popover * feat: use new macaw ui version * Types * Feature flag * fix: type errors * Alignment * Fix api * feat: add slug * feat: add slug * feat: add slug for the last time * fix: return slug from left options * Fix combobox * Force slug * Changeset * fix: serialize value --------- Co-authored-by: Krzysztof Żuraw <9116238+krzysztofzuraw@users.noreply.github.com>
This commit is contained in:
parent
a08d034e75
commit
198341cb41
27 changed files with 2200 additions and 49 deletions
5
.changeset/olive-bikes-switch.md
Normal file
5
.changeset/olive-bikes-switch.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-dashboard": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Prototype of the new filters for product listing page
|
14
package-lock.json
generated
14
package-lock.json
generated
|
@ -27,7 +27,7 @@
|
||||||
"@material-ui/lab": "^4.0.0-alpha.61",
|
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||||
"@material-ui/styles": "^4.11.4",
|
"@material-ui/styles": "^4.11.4",
|
||||||
"@reach/auto-id": "^0.16.0",
|
"@reach/auto-id": "^0.16.0",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.98",
|
"@saleor/macaw-ui": "0.8.0-pre.101",
|
||||||
"@saleor/sdk": "0.6.0",
|
"@saleor/sdk": "0.6.0",
|
||||||
"@sentry/react": "^6.0.0",
|
"@sentry/react": "^6.0.0",
|
||||||
"@types/faker": "^5.1.6",
|
"@types/faker": "^5.1.6",
|
||||||
|
@ -7840,9 +7840,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@saleor/macaw-ui": {
|
"node_modules/@saleor/macaw-ui": {
|
||||||
"version": "0.8.0-pre.98",
|
"version": "0.8.0-pre.101",
|
||||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.98.tgz",
|
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.101.tgz",
|
||||||
"integrity": "sha512-W/KCjRoVVr751JxPET/ebXt9im5lleCGAtydFcuDwmxoU11wTlyxHM25owsJJBpTq1L+jdrMyXO/vZ1FL06Osg==",
|
"integrity": "sha512-q68uQs33L8CEjQkZyfRIaljOiRNV6dyIMR211VYdWtAs60jsozwfwnoifPMz3m1DBBJbbaHjwsC7dD9AdlaR2w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dessert-box/react": "^0.4.0",
|
"@dessert-box/react": "^0.4.0",
|
||||||
"@floating-ui/react-dom-interactions": "^0.5.0",
|
"@floating-ui/react-dom-interactions": "^0.5.0",
|
||||||
|
@ -40651,9 +40651,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@saleor/macaw-ui": {
|
"@saleor/macaw-ui": {
|
||||||
"version": "0.8.0-pre.98",
|
"version": "0.8.0-pre.101",
|
||||||
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.98.tgz",
|
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.8.0-pre.101.tgz",
|
||||||
"integrity": "sha512-W/KCjRoVVr751JxPET/ebXt9im5lleCGAtydFcuDwmxoU11wTlyxHM25owsJJBpTq1L+jdrMyXO/vZ1FL06Osg==",
|
"integrity": "sha512-q68uQs33L8CEjQkZyfRIaljOiRNV6dyIMR211VYdWtAs60jsozwfwnoifPMz3m1DBBJbbaHjwsC7dD9AdlaR2w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@dessert-box/react": "^0.4.0",
|
"@dessert-box/react": "^0.4.0",
|
||||||
"@floating-ui/react-dom-interactions": "^0.5.0",
|
"@floating-ui/react-dom-interactions": "^0.5.0",
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
"@material-ui/lab": "^4.0.0-alpha.61",
|
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||||
"@material-ui/styles": "^4.11.4",
|
"@material-ui/styles": "^4.11.4",
|
||||||
"@reach/auto-id": "^0.16.0",
|
"@reach/auto-id": "^0.16.0",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.98",
|
"@saleor/macaw-ui": "0.8.0-pre.101",
|
||||||
"@saleor/sdk": "0.6.0",
|
"@saleor/sdk": "0.6.0",
|
||||||
"@sentry/react": "^6.0.0",
|
"@sentry/react": "^6.0.0",
|
||||||
"@types/faker": "^5.1.6",
|
"@types/faker": "^5.1.6",
|
||||||
|
@ -96,8 +96,6 @@
|
||||||
"use-react-router": "^1.0.7"
|
"use-react-router": "^1.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@changesets/changelog-github": "^0.4.8",
|
|
||||||
"@changesets/cli": "^2.26.1",
|
|
||||||
"@babel/cli": "^7.5.5",
|
"@babel/cli": "^7.5.5",
|
||||||
"@babel/core": "^7.7.7",
|
"@babel/core": "^7.7.7",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.5.0",
|
"@babel/plugin-proposal-class-properties": "^7.5.0",
|
||||||
|
@ -110,6 +108,8 @@
|
||||||
"@babel/preset-react": "^7.7.4",
|
"@babel/preset-react": "^7.7.4",
|
||||||
"@babel/preset-typescript": "^7.13.0",
|
"@babel/preset-typescript": "^7.13.0",
|
||||||
"@babel/runtime": "^7.7.6",
|
"@babel/runtime": "^7.7.6",
|
||||||
|
"@changesets/changelog-github": "^0.4.8",
|
||||||
|
"@changesets/cli": "^2.26.1",
|
||||||
"@editorjs/embed": "^2.5.3",
|
"@editorjs/embed": "^2.5.3",
|
||||||
"@esbuild-plugins/node-globals-polyfill": "^0.1.1",
|
"@esbuild-plugins/node-globals-polyfill": "^0.1.1",
|
||||||
"@formatjs/cli": "^4.5.0",
|
"@formatjs/cli": "^4.5.0",
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { FilterErrorMessages, IFilter } from "@dashboard/components/Filter";
|
import { FilterErrorMessages, IFilter } from "@dashboard/components/Filter";
|
||||||
|
import { useFlag } from "@dashboard/featureFlags";
|
||||||
import { FilterProps, SearchPageProps } from "@dashboard/types";
|
import { FilterProps, SearchPageProps } from "@dashboard/types";
|
||||||
import { Box } from "@saleor/macaw-ui/next";
|
import { Box } from "@saleor/macaw-ui/next";
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
|
|
||||||
|
import { ExpressionFilters } from "./components/ExpressionFilters";
|
||||||
import { FiltersSelect } from "./components/FiltersSelect";
|
import { FiltersSelect } from "./components/FiltersSelect";
|
||||||
import SearchInput from "./components/SearchInput";
|
import SearchInput from "./components/SearchInput";
|
||||||
|
|
||||||
|
@ -25,36 +27,45 @@ export const ListFilters = ({
|
||||||
onFilterAttributeFocus,
|
onFilterAttributeFocus,
|
||||||
errorMessages,
|
errorMessages,
|
||||||
actions,
|
actions,
|
||||||
}: ListFiltersProps) => (
|
}: ListFiltersProps) => {
|
||||||
<>
|
const isProductPage = window.location.pathname.includes("/products");
|
||||||
<Box
|
const productListingPageFiltersFlag = useFlag("product_filters");
|
||||||
display="grid"
|
const filtersEnabled = isProductPage && productListingPageFiltersFlag.enabled;
|
||||||
gridTemplateColumns={2}
|
|
||||||
gap={4}
|
|
||||||
paddingBottom={2}
|
|
||||||
paddingX={6}
|
|
||||||
>
|
|
||||||
<Box display="flex" alignItems="center" gap={4}>
|
|
||||||
<FiltersSelect
|
|
||||||
errorMessages={errorMessages}
|
|
||||||
menu={filterStructure}
|
|
||||||
currencySymbol={currencySymbol}
|
|
||||||
onFilterAdd={onFilterChange}
|
|
||||||
onFilterAttributeFocus={onFilterAttributeFocus}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Box __width="320px">
|
return (
|
||||||
<SearchInput
|
<>
|
||||||
initialSearch={initialSearch}
|
<Box
|
||||||
placeholder={searchPlaceholder}
|
display="grid"
|
||||||
onSearchChange={onSearchChange}
|
gridTemplateColumns={2}
|
||||||
/>
|
gap={4}
|
||||||
|
paddingBottom={2}
|
||||||
|
paddingX={6}
|
||||||
|
>
|
||||||
|
<Box display="flex" alignItems="center" gap={4}>
|
||||||
|
{filtersEnabled ? (
|
||||||
|
<ExpressionFilters />
|
||||||
|
) : (
|
||||||
|
<FiltersSelect
|
||||||
|
errorMessages={errorMessages}
|
||||||
|
menu={filterStructure}
|
||||||
|
currencySymbol={currencySymbol}
|
||||||
|
onFilterAdd={onFilterChange}
|
||||||
|
onFilterAttributeFocus={onFilterAttributeFocus}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Box __width="320px">
|
||||||
|
<SearchInput
|
||||||
|
initialSearch={initialSearch}
|
||||||
|
placeholder={searchPlaceholder}
|
||||||
|
onSearchChange={onSearchChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box display="flex" justifyContent="flex-end">
|
||||||
|
{actions}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" justifyContent="flex-end">
|
</>
|
||||||
{actions}
|
);
|
||||||
</Box>
|
};
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
ListFilters.displayName = "FilterBar";
|
ListFilters.displayName = "FilterBar";
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { ConditionalFilters } from "@dashboard/components/ConditionalFilter";
|
||||||
|
import { Box, Button, Popover } from "@saleor/macaw-ui/next";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export const ExpressionFilters = () => (
|
||||||
|
<Popover>
|
||||||
|
<Popover.Trigger>
|
||||||
|
<Button>Show filters</Button>
|
||||||
|
</Popover.Trigger>
|
||||||
|
<Popover.Content align="start">
|
||||||
|
<Box __minWidth="200px" __minHeight="100px" paddingX={4} paddingY={3}>
|
||||||
|
<Popover.Arrow />
|
||||||
|
<ConditionalFilters />
|
||||||
|
</Box>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover>
|
||||||
|
);
|
56
src/components/ConditionalFilter/API/InitialStateResponse.ts
Normal file
56
src/components/ConditionalFilter/API/InitialStateResponse.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { AttributeInputType } from "../FilterElement/ConditionOptions";
|
||||||
|
import { ItemOption } from "../FilterElement/ConditionSelected";
|
||||||
|
import { UrlToken } from "../ValueProvider/UrlToken";
|
||||||
|
|
||||||
|
interface AttributeDTO {
|
||||||
|
choices: Array<{ label: string; value: string; slug: string }>;
|
||||||
|
inputType: AttributeInputType;
|
||||||
|
label: string;
|
||||||
|
slug: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InitialStateResponse {
|
||||||
|
constructor(
|
||||||
|
public category: ItemOption[],
|
||||||
|
public attribute: Record<string, AttributeDTO>,
|
||||||
|
public channel: ItemOption[],
|
||||||
|
public collection: ItemOption[],
|
||||||
|
public producttype: ItemOption[],
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public attributeByName(name: string) {
|
||||||
|
return this.attribute[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public filterByUrlToken(token: UrlToken) {
|
||||||
|
if (token.isAttribute()) {
|
||||||
|
return this.attribute[token.name].choices.filter(({ value }) =>
|
||||||
|
token.value.includes(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token.isLoadable()) {
|
||||||
|
return [token.value] as string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getEntryByname(token.name).filter(
|
||||||
|
({ slug }) => slug && token.value.includes(slug),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getEntryByname(name: string) {
|
||||||
|
switch (name) {
|
||||||
|
case "category":
|
||||||
|
return this.category;
|
||||||
|
case "collection":
|
||||||
|
return this.collection;
|
||||||
|
case "producttype":
|
||||||
|
return this.producttype;
|
||||||
|
case "channel":
|
||||||
|
return this.channel;
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
211
src/components/ConditionalFilter/API/getAPIOptions.tsx
Normal file
211
src/components/ConditionalFilter/API/getAPIOptions.tsx
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
// @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";
|
||||||
|
|
||||||
|
const getFilterElement = (value: any, index: number): FilterElement => {
|
||||||
|
const possibleFilterElement = value[index];
|
||||||
|
return typeof possibleFilterElement != "string"
|
||||||
|
? possibleFilterElement
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getInitialRightOperatorOptions = async (
|
||||||
|
client: ApolloClient<any>,
|
||||||
|
position: string,
|
||||||
|
value: FilterContainer,
|
||||||
|
) => {
|
||||||
|
const index = parseInt(position, 10);
|
||||||
|
const filterElement = getFilterElement(value, index);
|
||||||
|
|
||||||
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRightOperatorOptionsByQuery = async (
|
||||||
|
client: ApolloClient<any>,
|
||||||
|
position: string,
|
||||||
|
value: FilterContainer,
|
||||||
|
inputValue: string,
|
||||||
|
) => {
|
||||||
|
const index = parseInt(position, 10);
|
||||||
|
const filterElement = getFilterElement(value, index);
|
||||||
|
|
||||||
|
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()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLeftOperatorOptions = async (
|
||||||
|
client: any,
|
||||||
|
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,
|
||||||
|
}));
|
||||||
|
};
|
177
src/components/ConditionalFilter/API/getInitalAPIState.tsx
Normal file
177
src/components/ConditionalFilter/API/getInitalAPIState.tsx
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
// @ts-strict-ignore
|
||||||
|
import { useApolloClient } from "@apollo/client";
|
||||||
|
import {
|
||||||
|
_GetChannelOperandsDocument,
|
||||||
|
_SearchAttributeOperandsDocument,
|
||||||
|
_SearchCategoriesOperandsDocument,
|
||||||
|
_SearchCollectionsOperandsDocument,
|
||||||
|
_SearchProductTypesOperandsDocument,
|
||||||
|
} from "@dashboard/graphql";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { InitialStateResponse } from "./InitialStateResponse";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
category?: string[];
|
||||||
|
collection?: string[];
|
||||||
|
channel?: string[];
|
||||||
|
producttype?: string[];
|
||||||
|
attribute?: {
|
||||||
|
[attribute: string]: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useInitialAPIState = ({
|
||||||
|
category = [],
|
||||||
|
collection = [],
|
||||||
|
producttype = [],
|
||||||
|
channel = [],
|
||||||
|
attribute = {},
|
||||||
|
}: Props) => {
|
||||||
|
const client = useApolloClient();
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const queriesToRun = [];
|
||||||
|
|
||||||
|
const fetchQueries = async () => {
|
||||||
|
const data = await Promise.all(queriesToRun);
|
||||||
|
setData(data);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (channel.length > 0) {
|
||||||
|
queriesToRun.push(
|
||||||
|
client.query({
|
||||||
|
query: _GetChannelOperandsDocument,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
queriesToRun.push({});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collection.length > 0) {
|
||||||
|
queriesToRun.push(
|
||||||
|
client.query({
|
||||||
|
query: _SearchCollectionsOperandsDocument,
|
||||||
|
variables: {
|
||||||
|
collectionsSlugs: collection,
|
||||||
|
first: collection.length,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
queriesToRun.push({});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category.length > 0) {
|
||||||
|
queriesToRun.push(
|
||||||
|
client.query({
|
||||||
|
query: _SearchCategoriesOperandsDocument,
|
||||||
|
variables: {
|
||||||
|
categoriesSlugs: category,
|
||||||
|
first: category.length,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
queriesToRun.push({});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (producttype.length > 0) {
|
||||||
|
queriesToRun.push(
|
||||||
|
client.query({
|
||||||
|
query: _SearchProductTypesOperandsDocument,
|
||||||
|
variables: {
|
||||||
|
productTypesSlugs: producttype,
|
||||||
|
first: producttype.length,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
queriesToRun.push({});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(attribute).length > 0) {
|
||||||
|
queriesToRun.push(
|
||||||
|
client.query({
|
||||||
|
query: _SearchAttributeOperandsDocument,
|
||||||
|
variables: {
|
||||||
|
attributesSlugs: Object.keys(attribute),
|
||||||
|
choicesIds: Object.values(attribute).flat(),
|
||||||
|
first: Object.keys(attribute).length,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
queriesToRun.push({});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchQueries();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [
|
||||||
|
channelData,
|
||||||
|
collectionData,
|
||||||
|
categoryData,
|
||||||
|
productTypesData,
|
||||||
|
attributesData,
|
||||||
|
] = data;
|
||||||
|
|
||||||
|
const channelPicks =
|
||||||
|
channelData?.data?.channels
|
||||||
|
?.filter(({ slug }) => channel.includes(slug))
|
||||||
|
.map(({ id, name }) => ({ label: name, value: id })) ?? [];
|
||||||
|
|
||||||
|
const collectionPicks =
|
||||||
|
collectionData?.data?.search?.edges.map(({ node }) => ({
|
||||||
|
label: node?.name,
|
||||||
|
value: node?.id,
|
||||||
|
slug: node?.slug,
|
||||||
|
})) ?? [];
|
||||||
|
|
||||||
|
const categoryPicks =
|
||||||
|
categoryData?.data?.search?.edges.map(({ node }) => ({
|
||||||
|
label: node?.name,
|
||||||
|
value: node?.id,
|
||||||
|
slug: node?.slug,
|
||||||
|
})) ?? [];
|
||||||
|
|
||||||
|
const productTypePicks =
|
||||||
|
productTypesData?.data?.search?.edges.map(({ node }) => ({
|
||||||
|
label: node?.name,
|
||||||
|
value: node?.id,
|
||||||
|
slug: node?.slug,
|
||||||
|
})) ?? [];
|
||||||
|
|
||||||
|
const attributePicks =
|
||||||
|
attributesData?.data?.search?.edges.reduce(
|
||||||
|
(acc, { node }) => ({
|
||||||
|
...acc,
|
||||||
|
[node?.slug]: {
|
||||||
|
choices: node?.choices.edges.map(({ node }) => ({
|
||||||
|
label: node?.name,
|
||||||
|
value: node?.id,
|
||||||
|
slug: node?.slug,
|
||||||
|
})),
|
||||||
|
slug: node?.slug,
|
||||||
|
value: node?.id,
|
||||||
|
label: node?.name,
|
||||||
|
inputType: node?.inputType,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
) ?? {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: new InitialStateResponse(
|
||||||
|
categoryPicks,
|
||||||
|
attributePicks,
|
||||||
|
channelPicks,
|
||||||
|
collectionPicks,
|
||||||
|
productTypePicks,
|
||||||
|
),
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
};
|
156
src/components/ConditionalFilter/API/queries.ts
Normal file
156
src/components/ConditionalFilter/API/queries.ts
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
import { gql } from "@apollo/client";
|
||||||
|
|
||||||
|
export const initialDynamicLeftOperands = gql`
|
||||||
|
query _GetDynamicLeftOperands($first: Int!, $query: String!) {
|
||||||
|
attributes(first: $first, filter: { type: PRODUCT_TYPE, search: $query }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
inputType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const initialDynamicOperands = gql`
|
||||||
|
query _GetChannelOperands {
|
||||||
|
channels {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query _SearchCollectionsOperands($first: Int!, $collectionsSlugs: [String!]) {
|
||||||
|
search: collections(first: $first, filter: { slugs: $collectionsSlugs }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query _SearchCategoriesOperands(
|
||||||
|
$after: String
|
||||||
|
$first: Int!
|
||||||
|
$categoriesSlugs: [String!]
|
||||||
|
) {
|
||||||
|
search: categories(
|
||||||
|
after: $after
|
||||||
|
first: $first
|
||||||
|
filter: { slugs: $categoriesSlugs }
|
||||||
|
) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query _SearchProductTypesOperands(
|
||||||
|
$after: String
|
||||||
|
$first: Int!
|
||||||
|
$productTypesSlugs: [String!]
|
||||||
|
) {
|
||||||
|
search: productTypes(
|
||||||
|
after: $after
|
||||||
|
first: $first
|
||||||
|
filter: { slugs: $productTypesSlugs }
|
||||||
|
) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query _SearchAttributeOperands(
|
||||||
|
$attributesSlugs: [String!]
|
||||||
|
$choicesIds: [ID!]
|
||||||
|
$first: Int!
|
||||||
|
) {
|
||||||
|
search: attributes(first: $first, filter: { slugs: $attributesSlugs }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
inputType
|
||||||
|
choices(first: 5, filter: { ids: $choicesIds }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
slug: id
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const dynamicOperandsQueries = gql`
|
||||||
|
query _GetAttributeChoices($slug: String!, $first: Int!, $query: String!) {
|
||||||
|
attribute(slug: $slug) {
|
||||||
|
choices(first: $first, filter: { search: $query }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
slug: id
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query _GetCollectionsChoices($first: Int!, $query: String!) {
|
||||||
|
collections(first: $first, filter: { search: $query }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query _GetCategoriesChoices($first: Int!, $query: String!) {
|
||||||
|
categories(first: $first, filter: { search: $query }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query _GetProductTypesChoices($first: Int!, $query: String!) {
|
||||||
|
productTypes(first: $first, filter: { search: $query }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
80
src/components/ConditionalFilter/FilterElement/Condition.ts
Normal file
80
src/components/ConditionalFilter/FilterElement/Condition.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
/* eslint-disable @typescript-eslint/member-ordering */
|
||||||
|
import { InitialStateResponse } from "../API/InitialStateResponse";
|
||||||
|
import { LeftOperand } from "./../useLeftOperands";
|
||||||
|
import { UrlToken } from "./../ValueProvider/UrlToken";
|
||||||
|
import { ConditionOptions } from "./ConditionOptions";
|
||||||
|
import { ConditionSelected } from "./ConditionSelected";
|
||||||
|
|
||||||
|
export class Condition {
|
||||||
|
private constructor(
|
||||||
|
public options: ConditionOptions,
|
||||||
|
public selected: ConditionSelected,
|
||||||
|
public loading: boolean,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public enableLoading() {
|
||||||
|
this.loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public disableLoading() {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isLoading() {
|
||||||
|
return this.loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createEmpty() {
|
||||||
|
return new Condition(
|
||||||
|
ConditionOptions.empty(),
|
||||||
|
ConditionSelected.empty(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static emptyFromLeftOperand(operand: LeftOperand) {
|
||||||
|
const options = ConditionOptions.fromName(operand.type);
|
||||||
|
|
||||||
|
return new Condition(
|
||||||
|
options,
|
||||||
|
ConditionSelected.fromConditionItem(options.first()),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromUrlToken(token: UrlToken, response: InitialStateResponse) {
|
||||||
|
if (ConditionOptions.isStaticName(token.name)) {
|
||||||
|
const staticOptions = ConditionOptions.fromStaticElementName(token.name);
|
||||||
|
const selectedOption = staticOptions.findByLabel(token.conditionKind);
|
||||||
|
const valueItems = response.filterByUrlToken(token);
|
||||||
|
const value =
|
||||||
|
selectedOption?.type === "multiselect" && valueItems.length > 0
|
||||||
|
? valueItems
|
||||||
|
: valueItems[0];
|
||||||
|
|
||||||
|
if (!selectedOption) {
|
||||||
|
return Condition.createEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Condition(
|
||||||
|
staticOptions,
|
||||||
|
ConditionSelected.fromConditionItemAndValue(selectedOption, value),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.isAttribute()) {
|
||||||
|
const attribute = response.attributeByName(token.name);
|
||||||
|
const options = ConditionOptions.fromAtributeType(attribute.inputType);
|
||||||
|
const value = response.filterByUrlToken(token);
|
||||||
|
|
||||||
|
return new Condition(
|
||||||
|
options,
|
||||||
|
ConditionSelected.fromConditionItemAndValue(options.first(), value),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Condition.createEmpty();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
import {
|
||||||
|
ATTRIBUTE_INPUT_TYPE_CONDITIONS,
|
||||||
|
STATIC_CONDITIONS,
|
||||||
|
} from "../constants";
|
||||||
|
|
||||||
|
export type StaticElementName = keyof typeof STATIC_CONDITIONS;
|
||||||
|
export type AttributeInputType = keyof typeof ATTRIBUTE_INPUT_TYPE_CONDITIONS;
|
||||||
|
|
||||||
|
export interface ConditionItem {
|
||||||
|
type: string;
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ConditionOptions extends Array<ConditionItem> {
|
||||||
|
private constructor(options: ConditionItem[] | number) {
|
||||||
|
if (Array.isArray(options)) {
|
||||||
|
super(...options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static isStaticName(name: string): name is StaticElementName {
|
||||||
|
return name in STATIC_CONDITIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static isAttributeInputType(name: string): name is AttributeInputType {
|
||||||
|
return name in ATTRIBUTE_INPUT_TYPE_CONDITIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromAtributeType(inputType: AttributeInputType) {
|
||||||
|
const options = ATTRIBUTE_INPUT_TYPE_CONDITIONS[inputType];
|
||||||
|
|
||||||
|
if (!options) {
|
||||||
|
throw new Error(`Unsupported attribute input type "${inputType}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConditionOptions(options);
|
||||||
|
}
|
||||||
|
public static fromStaticElementName(name: StaticElementName) {
|
||||||
|
const options = STATIC_CONDITIONS[name];
|
||||||
|
|
||||||
|
if (!options) {
|
||||||
|
throw new Error(`Unsupported static element "${name}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConditionOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromName(name: AttributeInputType | StaticElementName) {
|
||||||
|
const optionsStatic = this.isStaticName(name) && STATIC_CONDITIONS[name];
|
||||||
|
const optionsAttribute =
|
||||||
|
this.isAttributeInputType(name) && ATTRIBUTE_INPUT_TYPE_CONDITIONS[name];
|
||||||
|
|
||||||
|
if (optionsStatic) {
|
||||||
|
return new ConditionOptions(optionsStatic);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionsAttribute) {
|
||||||
|
return new ConditionOptions(optionsAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unsupported condition element "${name}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static empty() {
|
||||||
|
return new ConditionOptions([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public findByLabel(label: string) {
|
||||||
|
return this.find(f => f.label === label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public first() {
|
||||||
|
return this[0];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { getDefaultByControlName } from "../controlsType";
|
||||||
|
import { ConditionItem } from "./ConditionOptions";
|
||||||
|
|
||||||
|
export interface ItemOption {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
slug?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ConditionOption =
|
||||||
|
| ItemOption
|
||||||
|
| ItemOption[]
|
||||||
|
| string
|
||||||
|
| string[]
|
||||||
|
| [string, string];
|
||||||
|
|
||||||
|
export class ConditionSelected {
|
||||||
|
private constructor(
|
||||||
|
public value: ConditionOption,
|
||||||
|
public conditionValue: ConditionItem | null,
|
||||||
|
public options: ConditionOption[],
|
||||||
|
public loading: boolean,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static empty() {
|
||||||
|
return new ConditionSelected("", null, [], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromConditionItem(conditionItem: ConditionItem) {
|
||||||
|
return new ConditionSelected(
|
||||||
|
getDefaultByControlName(conditionItem.type),
|
||||||
|
conditionItem,
|
||||||
|
[],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromConditionItemAndValue(
|
||||||
|
conditionItem: ConditionItem,
|
||||||
|
value: ConditionOption,
|
||||||
|
) {
|
||||||
|
return new ConditionSelected(value, conditionItem, [], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enableLoading() {
|
||||||
|
this.loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public disableLoading() {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isLoading() {
|
||||||
|
return this.loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setValue(value: ConditionOption) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setOptions(options: ConditionOption[]) {
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
if (this.conditionValue) {
|
||||||
|
this.value = getDefaultByControlName(this.conditionValue.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
179
src/components/ConditionalFilter/FilterElement/FilterElement.ts
Normal file
179
src/components/ConditionalFilter/FilterElement/FilterElement.ts
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
/* eslint-disable @typescript-eslint/member-ordering */
|
||||||
|
import { InitialStateResponse } from "../API/InitialStateResponse";
|
||||||
|
import { LeftOperand } from "./../useLeftOperands";
|
||||||
|
import { CONDITIONS, UrlEntry, UrlToken } from "./../ValueProvider/UrlToken";
|
||||||
|
import { Condition } from "./Condition";
|
||||||
|
import { ConditionItem, ConditionOptions } from "./ConditionOptions";
|
||||||
|
import { ConditionOption, ConditionSelected } from "./ConditionSelected";
|
||||||
|
|
||||||
|
interface ExpressionValue {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createStaticEntry = (rawEntry: ConditionOption) => {
|
||||||
|
if (typeof rawEntry === "string") {
|
||||||
|
return rawEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(rawEntry)) {
|
||||||
|
return rawEntry.map(el => (typeof el === "string" ? el : el.slug));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawEntry.slug;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAttributeEntry = (rawEntry: ConditionOption) => {
|
||||||
|
if (typeof rawEntry === "string") {
|
||||||
|
return rawEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(rawEntry)) {
|
||||||
|
return rawEntry.map(el => (typeof el === "string" ? el : el.slug));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawEntry.slug;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class FilterElement {
|
||||||
|
private constructor(
|
||||||
|
public value: ExpressionValue,
|
||||||
|
public condition: Condition,
|
||||||
|
public loading: boolean,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public enableLoading() {
|
||||||
|
this.loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public disableLoading() {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isLoading() {
|
||||||
|
return this.loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateLeftOperator(leftOperand: LeftOperand) {
|
||||||
|
this.value = {
|
||||||
|
value: leftOperand.slug,
|
||||||
|
label: leftOperand.label,
|
||||||
|
type: leftOperand.type,
|
||||||
|
};
|
||||||
|
this.condition = Condition.emptyFromLeftOperand(leftOperand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateLeftLoadingState(loading: boolean) {
|
||||||
|
this.loading = loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateCondition(conditionValue: ConditionItem) {
|
||||||
|
this.condition.selected =
|
||||||
|
ConditionSelected.fromConditionItem(conditionValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateRightOperator(value: ConditionOption) {
|
||||||
|
this.condition.selected.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateRightOptions(options: ConditionOption[]) {
|
||||||
|
this.condition.selected.setOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateRightLoadingState(loading: boolean) {
|
||||||
|
if (loading) {
|
||||||
|
this.condition.selected.enableLoading();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.condition.selected.disableLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEmpty() {
|
||||||
|
return this.value.type === "e";
|
||||||
|
}
|
||||||
|
|
||||||
|
public isStatic() {
|
||||||
|
return ConditionOptions.isStaticName(this.value.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAttribute() {
|
||||||
|
return ConditionOptions.isAttributeInputType(this.value.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isCollection() {
|
||||||
|
return this.value.value === "collection";
|
||||||
|
}
|
||||||
|
|
||||||
|
public isCategory() {
|
||||||
|
return this.value.value === "category";
|
||||||
|
}
|
||||||
|
|
||||||
|
public isProductType() {
|
||||||
|
return this.value.value === "producttype";
|
||||||
|
}
|
||||||
|
|
||||||
|
public isChannel() {
|
||||||
|
return this.value.value === "channel";
|
||||||
|
}
|
||||||
|
|
||||||
|
public asUrlEntry(): UrlEntry {
|
||||||
|
const { conditionValue } = this.condition.selected;
|
||||||
|
const conditionIndex = CONDITIONS.findIndex(
|
||||||
|
el => conditionValue && el === conditionValue.label,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isAttribute()) {
|
||||||
|
return {
|
||||||
|
[`a${conditionIndex}.${this.value.value}`]: createAttributeEntry(
|
||||||
|
this.condition.selected.value,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
[`s${conditionIndex}.${this.value.value}`]: createStaticEntry(
|
||||||
|
this.condition.selected.value,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromValueEntry(valueEntry: any) {
|
||||||
|
return new FilterElement(valueEntry.value, valueEntry.condition, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createEmpty() {
|
||||||
|
return new FilterElement(
|
||||||
|
{ value: "", label: "", type: "s" },
|
||||||
|
Condition.createEmpty(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromUrlToken(token: UrlToken, response: InitialStateResponse) {
|
||||||
|
if (token.isStatic()) {
|
||||||
|
return new FilterElement(
|
||||||
|
{ value: token.name, label: token.name, type: token.name },
|
||||||
|
Condition.fromUrlToken(token, response),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.isAttribute()) {
|
||||||
|
const attribute = response.attributeByName(token.name);
|
||||||
|
|
||||||
|
return new FilterElement(
|
||||||
|
{
|
||||||
|
value: token.name,
|
||||||
|
label: attribute.label,
|
||||||
|
type: attribute.inputType,
|
||||||
|
},
|
||||||
|
Condition.fromUrlToken(token, response),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
2
src/components/ConditionalFilter/FilterElement/index.ts
Normal file
2
src/components/ConditionalFilter/FilterElement/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { FilterElement } from "./FilterElement"
|
||||||
|
export { Condition } from "./Condition"
|
7
src/components/ConditionalFilter/FilterValueProvider.ts
Normal file
7
src/components/ConditionalFilter/FilterValueProvider.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { FilterContainer } from "./useFilterContainer";
|
||||||
|
|
||||||
|
export interface FilterValueProvider {
|
||||||
|
value: FilterContainer;
|
||||||
|
loading: boolean;
|
||||||
|
persist: (newValue: FilterContainer) => void;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
// @ts-strict-ignore
|
||||||
|
import { UrlToken } from "../UrlToken";
|
||||||
|
|
||||||
|
export interface FetchingParams {
|
||||||
|
category: string[];
|
||||||
|
collection: string[];
|
||||||
|
channel: string[];
|
||||||
|
producttype: [];
|
||||||
|
attribute: Record<string, string[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const emptyFetchingParams: FetchingParams = {
|
||||||
|
category: [],
|
||||||
|
collection: [],
|
||||||
|
channel: [],
|
||||||
|
producttype: [],
|
||||||
|
attribute: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const unique = <T>(array: Iterable<T>) => Array.from(new Set(array));
|
||||||
|
|
||||||
|
export const toFetchingParams = (p: FetchingParams, c: UrlToken) => {
|
||||||
|
if (!c.isAttribute() && !p[c.name]) {
|
||||||
|
p[c.name] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.isAttribute() && !p.attribute[c.name]) {
|
||||||
|
p.attribute[c.name] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.isAttribute()) {
|
||||||
|
p.attribute[c.name] = unique(p.attribute[c.name].concat(c.value));
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
p[c.name] = unique(p[c.name].concat(c.value));
|
||||||
|
|
||||||
|
return p;
|
||||||
|
};
|
|
@ -0,0 +1,106 @@
|
||||||
|
// @ts-strict-ignore
|
||||||
|
|
||||||
|
import { parse, ParsedQs } from "qs";
|
||||||
|
import { useRef } from "react";
|
||||||
|
|
||||||
|
import { InitialStateResponse } from "../../API/InitialStateResponse";
|
||||||
|
import { FilterElement } from "../../FilterElement";
|
||||||
|
import { FilterContainer } from "../../useFilterContainer";
|
||||||
|
import { UrlToken } from "../UrlToken";
|
||||||
|
import {
|
||||||
|
emptyFetchingParams,
|
||||||
|
FetchingParams,
|
||||||
|
toFetchingParams,
|
||||||
|
} from "./fetchingParams";
|
||||||
|
|
||||||
|
const toFlatUrlTokens = (p: UrlToken[], c: TokenArray[number]) => {
|
||||||
|
if (typeof c == "string") {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(c)) {
|
||||||
|
return p.concat(flatenate(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.concat(c);
|
||||||
|
};
|
||||||
|
|
||||||
|
const flatenate = (tokens: TokenArray): UrlToken[] =>
|
||||||
|
tokens.reduce<UrlToken[]>(toFlatUrlTokens, []);
|
||||||
|
|
||||||
|
const mapToTokens = (urlEntries: Array<ParsedQs | string>): TokenArray =>
|
||||||
|
urlEntries.map(entry => {
|
||||||
|
if (typeof entry === "string") {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(entry)) {
|
||||||
|
return mapToTokens(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return UrlToken.fromUrlEntry(entry);
|
||||||
|
}) as TokenArray;
|
||||||
|
|
||||||
|
const tokenizeUrl = (urlParams: string) => {
|
||||||
|
const parsedUrl = Object.values(parse(urlParams)) as Array<ParsedQs | string>;
|
||||||
|
|
||||||
|
return mapToTokens(parsedUrl);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapUrlTokensToFilterValues = (
|
||||||
|
urlTokens: TokenArray,
|
||||||
|
response: InitialStateResponse,
|
||||||
|
) =>
|
||||||
|
urlTokens.map(el => {
|
||||||
|
if (typeof el === "string") {
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(el)) {
|
||||||
|
return mapUrlTokensToFilterValues(el, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FilterElement.fromUrlToken(el, response);
|
||||||
|
});
|
||||||
|
|
||||||
|
export class TokenArray extends Array<string | UrlToken | TokenArray> {
|
||||||
|
constructor(url: string) {
|
||||||
|
super(...tokenizeUrl(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFetchingParams() {
|
||||||
|
return this.asFlatArray()
|
||||||
|
.filter(token => token.isLoadable())
|
||||||
|
.reduce<FetchingParams>(toFetchingParams, emptyFetchingParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public asFlatArray() {
|
||||||
|
return flatenate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public asFilterValuesFromResponse(
|
||||||
|
response: InitialStateResponse,
|
||||||
|
): FilterContainer {
|
||||||
|
return this.map(el => {
|
||||||
|
if (typeof el === "string") {
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(el)) {
|
||||||
|
return mapUrlTokensToFilterValues(el, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FilterElement.fromUrlToken(el, response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTokenArray = (url: string) => {
|
||||||
|
const instance = useRef<TokenArray>(null);
|
||||||
|
|
||||||
|
if (!instance.current) {
|
||||||
|
instance.current = new TokenArray(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance.current;
|
||||||
|
};
|
47
src/components/ConditionalFilter/ValueProvider/UrlToken.ts
Normal file
47
src/components/ConditionalFilter/ValueProvider/UrlToken.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// @ts-strict-ignore
|
||||||
|
|
||||||
|
export const CONDITIONS = ["is", "equals", "in", "between", "lower", "greater"];
|
||||||
|
|
||||||
|
const STATIC_TO_LOAD = ["category", "collection", "channel", "producttype"];
|
||||||
|
|
||||||
|
type TokenType = "a" | "s";
|
||||||
|
|
||||||
|
// export type UrlEntry = Record<string, string | string[]>
|
||||||
|
|
||||||
|
export class UrlEntry {
|
||||||
|
constructor(key: string, value: string | string[]) {
|
||||||
|
this[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UrlToken {
|
||||||
|
private constructor(
|
||||||
|
public name: string,
|
||||||
|
public value: string | string[],
|
||||||
|
public type: TokenType,
|
||||||
|
public conditionKind: string,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static fromUrlEntry(entry: UrlEntry) {
|
||||||
|
const [key, value] = Object.entries(entry)[0] as [
|
||||||
|
string,
|
||||||
|
string | string[],
|
||||||
|
];
|
||||||
|
const [identifier, entryName] = key.split(".");
|
||||||
|
const [type, control] = identifier.split("") as [TokenType, string];
|
||||||
|
|
||||||
|
return new UrlToken(entryName, value, type, CONDITIONS[control]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isStatic() {
|
||||||
|
return this.type === "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAttribute() {
|
||||||
|
return this.type === "a";
|
||||||
|
}
|
||||||
|
|
||||||
|
public isLoadable() {
|
||||||
|
return STATIC_TO_LOAD.includes(this.name) || this.isAttribute();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
// @ts-strict-ignore
|
||||||
|
|
||||||
|
import { stringify } from "qs";
|
||||||
|
import useRouter from "use-react-router";
|
||||||
|
|
||||||
|
import { useInitialAPIState } from "../API/getInitalAPIState";
|
||||||
|
import { FilterValueProvider } from "../FilterValueProvider";
|
||||||
|
import { FilterContainer } from "../useFilterContainer";
|
||||||
|
import { useTokenArray } from "./TokenArray";
|
||||||
|
|
||||||
|
const prepareStructure = filterValue =>
|
||||||
|
filterValue.map(f => {
|
||||||
|
if (typeof f === "string") {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(f)) {
|
||||||
|
return prepareStructure(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.asUrlEntry();
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
exampple url: http://localhost:9000/dashboard/products/?0%5Bs2.category%5D%5B0%5D=accessories&0%5Bs2.category%5D%5B1%5D=groceries&1=o&2%5Ba2.abv%5D%5B0%5D=QXR0cmlidXRlVmFsdWU6Njg%3D&3=a&4%5Bs2.collection%5D%5B0%5D=featured-products&5=a&6%5Bs2.producttype%5D%5B0%5D=beer&7=a&8%5B0%5D%5Bs2.category%5D%5B0%5D=apparel&8%5B1%5D=o&8%5B2%5D%5Ba2.bottle-size%5D%5B0%5D=QXR0cmlidXRlVmFsdWU6NDY%3D&8%5B2%5D%5Ba2.bottle-size%5D%5B1%5D=QXR0cmlidXRlVmFsdWU6NDc%3D&asc=true&sort=name
|
||||||
|
*/
|
||||||
|
export const useUrlValueProvider = (): FilterValueProvider => {
|
||||||
|
const router = useRouter();
|
||||||
|
const params = new URLSearchParams(router.location.search);
|
||||||
|
params.delete("asc");
|
||||||
|
params.delete("sort");
|
||||||
|
|
||||||
|
const tokenizedUrl = useTokenArray(params.toString());
|
||||||
|
const fetchingParams = tokenizedUrl.getFetchingParams();
|
||||||
|
const { data, loading } = useInitialAPIState(fetchingParams);
|
||||||
|
const value = loading ? [] : tokenizedUrl.asFilterValuesFromResponse(data);
|
||||||
|
|
||||||
|
|
||||||
|
const persist = (filterValue: FilterContainer) => {
|
||||||
|
router.history.replace({
|
||||||
|
pathname: router.location.pathname,
|
||||||
|
search: stringify(prepareStructure(filterValue)),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
loading,
|
||||||
|
persist,
|
||||||
|
};
|
||||||
|
};
|
30
src/components/ConditionalFilter/constants.ts
Normal file
30
src/components/ConditionalFilter/constants.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
export const STATIC_CONDITIONS = {
|
||||||
|
category: [
|
||||||
|
{ type: "combobox", label: "is", value: "input-1" },
|
||||||
|
{ type: "multiselect", label: "in", value: "input-2" },
|
||||||
|
],
|
||||||
|
price: [
|
||||||
|
{ type: "number", label: "is", value: "input-1" },
|
||||||
|
{ type: "number", label: "lower", value: "input-2" },
|
||||||
|
{ type: "number", label: "greater", value: "input-3" },
|
||||||
|
{ type: "number.range", label: "between", 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" }],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ATTRIBUTE_INPUT_TYPE_CONDITIONS = {
|
||||||
|
DROPDOWN: [{ type: "multiselect", label: "in", value: "input-2" }],
|
||||||
|
MULTISELECT: [{ type: "multiselect", label: "in", value: "input-2" }],
|
||||||
|
BOOLEAN: [{ type: "select", label: "is", value: "input-5" }],
|
||||||
|
NUMERIC: [
|
||||||
|
{ type: "number", label: "is", value: "input-1" },
|
||||||
|
{ type: "number", label: "lower", value: "input-2" },
|
||||||
|
{ type: "number", label: "greater", value: "input-3" },
|
||||||
|
{ type: "number.range", label: "between", value: "input-4" },
|
||||||
|
],
|
||||||
|
DATE_TIME: [{ type: "date", label: "is", value: "input-1" }],
|
||||||
|
DATE: [{ type: "date", label: "is", value: "input-1" }],
|
||||||
|
SWATCH: [{ type: "multiselect", label: "in", value: "input-2" }],
|
||||||
|
};
|
14
src/components/ConditionalFilter/controlsType.ts
Normal file
14
src/components/ConditionalFilter/controlsType.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// @ts-strict-ignore
|
||||||
|
import { ConditionOption } from "./FilterElement/ConditionSelected";
|
||||||
|
|
||||||
|
export const CONTROL_DEFAULTS = {
|
||||||
|
text: "",
|
||||||
|
number: "",
|
||||||
|
"number.range": [] as unknown as [string, string],
|
||||||
|
multiselect: [] as ConditionOption[],
|
||||||
|
select: "",
|
||||||
|
combobox: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDefaultByControlName = (name: string): ConditionOption =>
|
||||||
|
CONTROL_DEFAULTS[name];
|
142
src/components/ConditionalFilter/index.tsx
Normal file
142
src/components/ConditionalFilter/index.tsx
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
// @ts-strict-ignore
|
||||||
|
import { useApolloClient } from "@apollo/client";
|
||||||
|
import useDebounce from "@dashboard/hooks/useDebounce";
|
||||||
|
import { _ExperimentalFilters, Box, Text } from "@saleor/macaw-ui/next";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getInitialRightOperatorOptions,
|
||||||
|
getLeftOperatorOptions,
|
||||||
|
getRightOperatorOptionsByQuery,
|
||||||
|
} from "./API/getAPIOptions";
|
||||||
|
import { useFilterContainer } from "./useFilterContainer";
|
||||||
|
import { useLeftOperands } from "./useLeftOperands";
|
||||||
|
import { useUrlValueProvider } from "./ValueProvider/useUrlValueProvider";
|
||||||
|
|
||||||
|
const FiltersArea = ({ provider, onConfirm }) => {
|
||||||
|
const client = useApolloClient();
|
||||||
|
|
||||||
|
const {
|
||||||
|
value,
|
||||||
|
addEmpty,
|
||||||
|
removeAt,
|
||||||
|
updateLeftOperator,
|
||||||
|
updateRightOperator,
|
||||||
|
updateCondition,
|
||||||
|
updateRightOptions,
|
||||||
|
updateRightLoadingState,
|
||||||
|
updateLeftLoadingState,
|
||||||
|
} = useFilterContainer(provider);
|
||||||
|
|
||||||
|
const { operands, setOperands } = useLeftOperands();
|
||||||
|
|
||||||
|
const handleLeftOperatorInputValueChange = (event: any) => {
|
||||||
|
const fetchAPI = async () => {
|
||||||
|
const options = await getLeftOperatorOptions(client, event.value);
|
||||||
|
setOperands(options);
|
||||||
|
};
|
||||||
|
updateLeftLoadingState(event.path, true);
|
||||||
|
fetchAPI();
|
||||||
|
updateLeftLoadingState(event.path, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLeftOperatorInputValueChangeDebounced = useDebounce(
|
||||||
|
handleLeftOperatorInputValueChange,
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRightOperatorInputValueChange = (event: any) => {
|
||||||
|
const fetchAPI = async () => {
|
||||||
|
const options = await getRightOperatorOptionsByQuery(
|
||||||
|
client,
|
||||||
|
event.path.split(".")[0],
|
||||||
|
value,
|
||||||
|
event.value,
|
||||||
|
);
|
||||||
|
updateRightOptions(event.path.split(".")[0], options);
|
||||||
|
};
|
||||||
|
updateRightLoadingState(event.path.split(".")[0], true);
|
||||||
|
fetchAPI();
|
||||||
|
updateRightLoadingState(event.path.split(".")[0], false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRightOperatorInputValueChangeDebounced = useDebounce(
|
||||||
|
handleRightOperatorInputValueChange,
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleStateChange = async event => {
|
||||||
|
if (event.type === "row.add") {
|
||||||
|
addEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === "row.remove") {
|
||||||
|
removeAt(event.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === "leftOperator.onChange") {
|
||||||
|
updateLeftOperator(event.path, event.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === "condition.onChange") {
|
||||||
|
updateCondition(event.path.split(".")[0], event.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === "rightOperator.onChange") {
|
||||||
|
updateRightOperator(event.path.split(".")[0], event.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === "rightOperator.onFocus") {
|
||||||
|
const path = event.path.split(".")[0];
|
||||||
|
updateRightLoadingState(path, true);
|
||||||
|
const options = await getInitialRightOperatorOptions(client, path, value);
|
||||||
|
updateRightOptions(path, options);
|
||||||
|
updateRightLoadingState(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === "rightOperator.onInputValueChange") {
|
||||||
|
handleRightOperatorInputValueChangeDebounced(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === "leftOperator.onInputValueChange") {
|
||||||
|
handleLeftOperatorInputValueChangeDebounced(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirm = () => onConfirm(value);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<_ExperimentalFilters
|
||||||
|
leftOptions={operands}
|
||||||
|
// @ts-ignore
|
||||||
|
value={value}
|
||||||
|
onChange={handleStateChange}
|
||||||
|
>
|
||||||
|
<_ExperimentalFilters.Footer>
|
||||||
|
<_ExperimentalFilters.AddRowButton>
|
||||||
|
Add new row
|
||||||
|
</_ExperimentalFilters.AddRowButton>
|
||||||
|
<_ExperimentalFilters.ConfirmButton onClick={handleConfirm}>
|
||||||
|
Confirm
|
||||||
|
</_ExperimentalFilters.ConfirmButton>
|
||||||
|
</_ExperimentalFilters.Footer>
|
||||||
|
</_ExperimentalFilters>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ConditionalFilters = () => {
|
||||||
|
const provider = useUrlValueProvider();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{provider.loading ? (
|
||||||
|
<Text>Loading...</Text>
|
||||||
|
) : (
|
||||||
|
// @ts-ignore
|
||||||
|
<FiltersArea provider={provider.value} onConfirm={provider.persist} />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
125
src/components/ConditionalFilter/useFilterContainer.ts
Normal file
125
src/components/ConditionalFilter/useFilterContainer.ts
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
// @ts-strict-ignore
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { FilterElement } from "./FilterElement";
|
||||||
|
|
||||||
|
export type FilterContainer = Array<string | FilterElement | FilterContainer>;
|
||||||
|
|
||||||
|
export const useFilterContainer = (initialValue: FilterContainer) => {
|
||||||
|
const [value, setValue] = useState(initialValue);
|
||||||
|
|
||||||
|
const addEmpty = () => {
|
||||||
|
const newValue = [];
|
||||||
|
if (value.length > 0) {
|
||||||
|
newValue.push("OR");
|
||||||
|
}
|
||||||
|
|
||||||
|
newValue.push(FilterElement.createEmpty());
|
||||||
|
|
||||||
|
setValue(v => v.concat(newValue));
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeAt = (position: string) => {
|
||||||
|
const index = parseInt(position, 10);
|
||||||
|
|
||||||
|
if (value.length > 0) {
|
||||||
|
setValue(v =>
|
||||||
|
v.filter((_, elIndex) => ![index - 1, index].includes(elIndex)),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(v => v.filter((_, elIndex) => ![index].includes(elIndex)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLeftOperator = (position: string, leftOperator: any) => {
|
||||||
|
const index = parseInt(position, 10);
|
||||||
|
setValue(v =>
|
||||||
|
v.map((el, elIndex) => {
|
||||||
|
if (elIndex === index && typeof el != "string" && !Array.isArray(el)) {
|
||||||
|
el.updateLeftOperator(leftOperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLeftLoadingState = (position: string, loading: boolean) => {
|
||||||
|
const index = parseInt(position, 10);
|
||||||
|
setValue(v =>
|
||||||
|
v.map((el, elIndex) => {
|
||||||
|
if (elIndex === index && typeof el != "string" && !Array.isArray(el)) {
|
||||||
|
el.updateLeftLoadingState(loading);
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateRightOperator = (position: string, leftOperator: any) => {
|
||||||
|
const index = parseInt(position, 10);
|
||||||
|
setValue(v =>
|
||||||
|
v.map((el, elIndex) => {
|
||||||
|
if (elIndex === index && typeof el != "string" && !Array.isArray(el)) {
|
||||||
|
el.updateRightOperator(leftOperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateRightOptions = (position: string, options: any) => {
|
||||||
|
const index = parseInt(position, 10);
|
||||||
|
setValue(v =>
|
||||||
|
v.map((el, elIndex) => {
|
||||||
|
if (elIndex === index && typeof el != "string" && !Array.isArray(el)) {
|
||||||
|
el.updateRightOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateRightLoadingState = (position: string, loading: boolean) => {
|
||||||
|
const index = parseInt(position, 10);
|
||||||
|
setValue(v =>
|
||||||
|
v.map((el, elIndex) => {
|
||||||
|
if (elIndex === index && typeof el != "string" && !Array.isArray(el)) {
|
||||||
|
el.updateRightLoadingState(loading);
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateCondition = (position: string, conditionValue: any) => {
|
||||||
|
const index = parseInt(position, 10);
|
||||||
|
|
||||||
|
setValue(v =>
|
||||||
|
v.map((el, elIndex) => {
|
||||||
|
if (elIndex === index && typeof el != "string" && !Array.isArray(el)) {
|
||||||
|
el.updateCondition(conditionValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
addEmpty,
|
||||||
|
removeAt,
|
||||||
|
updateLeftOperator,
|
||||||
|
updateRightOperator,
|
||||||
|
updateCondition,
|
||||||
|
updateRightOptions,
|
||||||
|
updateRightLoadingState,
|
||||||
|
updateLeftLoadingState,
|
||||||
|
};
|
||||||
|
};
|
34
src/components/ConditionalFilter/useLeftOperands.ts
Normal file
34
src/components/ConditionalFilter/useLeftOperands.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
AttributeInputType,
|
||||||
|
StaticElementName,
|
||||||
|
} from "./FilterElement/ConditionOptions";
|
||||||
|
|
||||||
|
export interface LeftOperand {
|
||||||
|
type: AttributeInputType | StaticElementName;
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 useLeftOperands = () => {
|
||||||
|
const [operands, setOperands] = useState<LeftOperand[]>(STATIC_OPTIONS);
|
||||||
|
|
||||||
|
return {
|
||||||
|
operands,
|
||||||
|
setOperands,
|
||||||
|
};
|
||||||
|
};
|
|
@ -24,16 +24,10 @@ const AVAILABLE_FLAGS = [
|
||||||
*/
|
*/
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "flag1",
|
name: "product_filters",
|
||||||
displayName: "Flag 1",
|
displayName: "Product filters",
|
||||||
description: "some description",
|
description: "New filters on product listing page",
|
||||||
content: { enabled: false, payload: "default" },
|
content: { enabled: false, payload: "" },
|
||||||
} as const,
|
|
||||||
{
|
|
||||||
name: "flag2",
|
|
||||||
displayName: "Flag 2",
|
|
||||||
description: "some description 2",
|
|
||||||
content: { enabled: false, payload: "default2" },
|
|
||||||
} as const,
|
} as const,
|
||||||
] satisfies FlagDefinition[];
|
] satisfies FlagDefinition[];
|
||||||
|
|
||||||
|
|
|
@ -5417,6 +5417,445 @@ export function useAddressValidationRulesLazyQuery(baseOptions?: ApolloReactHook
|
||||||
export type AddressValidationRulesQueryHookResult = ReturnType<typeof useAddressValidationRulesQuery>;
|
export type AddressValidationRulesQueryHookResult = ReturnType<typeof useAddressValidationRulesQuery>;
|
||||||
export type AddressValidationRulesLazyQueryHookResult = ReturnType<typeof useAddressValidationRulesLazyQuery>;
|
export type AddressValidationRulesLazyQueryHookResult = ReturnType<typeof useAddressValidationRulesLazyQuery>;
|
||||||
export type AddressValidationRulesQueryResult = Apollo.QueryResult<Types.AddressValidationRulesQuery, Types.AddressValidationRulesQueryVariables>;
|
export type AddressValidationRulesQueryResult = Apollo.QueryResult<Types.AddressValidationRulesQuery, Types.AddressValidationRulesQueryVariables>;
|
||||||
|
export const _GetDynamicLeftOperandsDocument = gql`
|
||||||
|
query _GetDynamicLeftOperands($first: Int!, $query: String!) {
|
||||||
|
attributes(first: $first, filter: {type: PRODUCT_TYPE, search: $query}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
inputType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_GetDynamicLeftOperandsQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_GetDynamicLeftOperandsQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_GetDynamicLeftOperandsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_GetDynamicLeftOperandsQuery({
|
||||||
|
* variables: {
|
||||||
|
* first: // value for 'first'
|
||||||
|
* query: // value for 'query'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_GetDynamicLeftOperandsQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types._GetDynamicLeftOperandsQuery, Types._GetDynamicLeftOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._GetDynamicLeftOperandsQuery, Types._GetDynamicLeftOperandsQueryVariables>(_GetDynamicLeftOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export function use_GetDynamicLeftOperandsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._GetDynamicLeftOperandsQuery, Types._GetDynamicLeftOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._GetDynamicLeftOperandsQuery, Types._GetDynamicLeftOperandsQueryVariables>(_GetDynamicLeftOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export type _GetDynamicLeftOperandsQueryHookResult = ReturnType<typeof use_GetDynamicLeftOperandsQuery>;
|
||||||
|
export type _GetDynamicLeftOperandsLazyQueryHookResult = ReturnType<typeof use_GetDynamicLeftOperandsLazyQuery>;
|
||||||
|
export type _GetDynamicLeftOperandsQueryResult = Apollo.QueryResult<Types._GetDynamicLeftOperandsQuery, Types._GetDynamicLeftOperandsQueryVariables>;
|
||||||
|
export const _GetChannelOperandsDocument = gql`
|
||||||
|
query _GetChannelOperands {
|
||||||
|
channels {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_GetChannelOperandsQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_GetChannelOperandsQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_GetChannelOperandsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_GetChannelOperandsQuery({
|
||||||
|
* variables: {
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_GetChannelOperandsQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types._GetChannelOperandsQuery, Types._GetChannelOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._GetChannelOperandsQuery, Types._GetChannelOperandsQueryVariables>(_GetChannelOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export function use_GetChannelOperandsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._GetChannelOperandsQuery, Types._GetChannelOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._GetChannelOperandsQuery, Types._GetChannelOperandsQueryVariables>(_GetChannelOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export type _GetChannelOperandsQueryHookResult = ReturnType<typeof use_GetChannelOperandsQuery>;
|
||||||
|
export type _GetChannelOperandsLazyQueryHookResult = ReturnType<typeof use_GetChannelOperandsLazyQuery>;
|
||||||
|
export type _GetChannelOperandsQueryResult = Apollo.QueryResult<Types._GetChannelOperandsQuery, Types._GetChannelOperandsQueryVariables>;
|
||||||
|
export const _SearchCollectionsOperandsDocument = gql`
|
||||||
|
query _SearchCollectionsOperands($first: Int!, $collectionsSlugs: [String!]) {
|
||||||
|
search: collections(first: $first, filter: {slugs: $collectionsSlugs}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_SearchCollectionsOperandsQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_SearchCollectionsOperandsQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_SearchCollectionsOperandsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_SearchCollectionsOperandsQuery({
|
||||||
|
* variables: {
|
||||||
|
* first: // value for 'first'
|
||||||
|
* collectionsSlugs: // value for 'collectionsSlugs'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_SearchCollectionsOperandsQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types._SearchCollectionsOperandsQuery, Types._SearchCollectionsOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._SearchCollectionsOperandsQuery, Types._SearchCollectionsOperandsQueryVariables>(_SearchCollectionsOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export function use_SearchCollectionsOperandsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._SearchCollectionsOperandsQuery, Types._SearchCollectionsOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._SearchCollectionsOperandsQuery, Types._SearchCollectionsOperandsQueryVariables>(_SearchCollectionsOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export type _SearchCollectionsOperandsQueryHookResult = ReturnType<typeof use_SearchCollectionsOperandsQuery>;
|
||||||
|
export type _SearchCollectionsOperandsLazyQueryHookResult = ReturnType<typeof use_SearchCollectionsOperandsLazyQuery>;
|
||||||
|
export type _SearchCollectionsOperandsQueryResult = Apollo.QueryResult<Types._SearchCollectionsOperandsQuery, Types._SearchCollectionsOperandsQueryVariables>;
|
||||||
|
export const _SearchCategoriesOperandsDocument = gql`
|
||||||
|
query _SearchCategoriesOperands($after: String, $first: Int!, $categoriesSlugs: [String!]) {
|
||||||
|
search: categories(
|
||||||
|
after: $after
|
||||||
|
first: $first
|
||||||
|
filter: {slugs: $categoriesSlugs}
|
||||||
|
) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_SearchCategoriesOperandsQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_SearchCategoriesOperandsQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_SearchCategoriesOperandsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_SearchCategoriesOperandsQuery({
|
||||||
|
* variables: {
|
||||||
|
* after: // value for 'after'
|
||||||
|
* first: // value for 'first'
|
||||||
|
* categoriesSlugs: // value for 'categoriesSlugs'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_SearchCategoriesOperandsQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types._SearchCategoriesOperandsQuery, Types._SearchCategoriesOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._SearchCategoriesOperandsQuery, Types._SearchCategoriesOperandsQueryVariables>(_SearchCategoriesOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export function use_SearchCategoriesOperandsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._SearchCategoriesOperandsQuery, Types._SearchCategoriesOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._SearchCategoriesOperandsQuery, Types._SearchCategoriesOperandsQueryVariables>(_SearchCategoriesOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export type _SearchCategoriesOperandsQueryHookResult = ReturnType<typeof use_SearchCategoriesOperandsQuery>;
|
||||||
|
export type _SearchCategoriesOperandsLazyQueryHookResult = ReturnType<typeof use_SearchCategoriesOperandsLazyQuery>;
|
||||||
|
export type _SearchCategoriesOperandsQueryResult = Apollo.QueryResult<Types._SearchCategoriesOperandsQuery, Types._SearchCategoriesOperandsQueryVariables>;
|
||||||
|
export const _SearchProductTypesOperandsDocument = gql`
|
||||||
|
query _SearchProductTypesOperands($after: String, $first: Int!, $productTypesSlugs: [String!]) {
|
||||||
|
search: productTypes(
|
||||||
|
after: $after
|
||||||
|
first: $first
|
||||||
|
filter: {slugs: $productTypesSlugs}
|
||||||
|
) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_SearchProductTypesOperandsQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_SearchProductTypesOperandsQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_SearchProductTypesOperandsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_SearchProductTypesOperandsQuery({
|
||||||
|
* variables: {
|
||||||
|
* after: // value for 'after'
|
||||||
|
* first: // value for 'first'
|
||||||
|
* productTypesSlugs: // value for 'productTypesSlugs'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_SearchProductTypesOperandsQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types._SearchProductTypesOperandsQuery, Types._SearchProductTypesOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._SearchProductTypesOperandsQuery, Types._SearchProductTypesOperandsQueryVariables>(_SearchProductTypesOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export function use_SearchProductTypesOperandsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._SearchProductTypesOperandsQuery, Types._SearchProductTypesOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._SearchProductTypesOperandsQuery, Types._SearchProductTypesOperandsQueryVariables>(_SearchProductTypesOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export type _SearchProductTypesOperandsQueryHookResult = ReturnType<typeof use_SearchProductTypesOperandsQuery>;
|
||||||
|
export type _SearchProductTypesOperandsLazyQueryHookResult = ReturnType<typeof use_SearchProductTypesOperandsLazyQuery>;
|
||||||
|
export type _SearchProductTypesOperandsQueryResult = Apollo.QueryResult<Types._SearchProductTypesOperandsQuery, Types._SearchProductTypesOperandsQueryVariables>;
|
||||||
|
export const _SearchAttributeOperandsDocument = gql`
|
||||||
|
query _SearchAttributeOperands($attributesSlugs: [String!], $choicesIds: [ID!], $first: Int!) {
|
||||||
|
search: attributes(first: $first, filter: {slugs: $attributesSlugs}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
inputType
|
||||||
|
choices(first: 5, filter: {ids: $choicesIds}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
slug: id
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_SearchAttributeOperandsQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_SearchAttributeOperandsQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_SearchAttributeOperandsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_SearchAttributeOperandsQuery({
|
||||||
|
* variables: {
|
||||||
|
* attributesSlugs: // value for 'attributesSlugs'
|
||||||
|
* choicesIds: // value for 'choicesIds'
|
||||||
|
* first: // value for 'first'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_SearchAttributeOperandsQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types._SearchAttributeOperandsQuery, Types._SearchAttributeOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._SearchAttributeOperandsQuery, Types._SearchAttributeOperandsQueryVariables>(_SearchAttributeOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export function use_SearchAttributeOperandsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._SearchAttributeOperandsQuery, Types._SearchAttributeOperandsQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._SearchAttributeOperandsQuery, Types._SearchAttributeOperandsQueryVariables>(_SearchAttributeOperandsDocument, options);
|
||||||
|
}
|
||||||
|
export type _SearchAttributeOperandsQueryHookResult = ReturnType<typeof use_SearchAttributeOperandsQuery>;
|
||||||
|
export type _SearchAttributeOperandsLazyQueryHookResult = ReturnType<typeof use_SearchAttributeOperandsLazyQuery>;
|
||||||
|
export type _SearchAttributeOperandsQueryResult = Apollo.QueryResult<Types._SearchAttributeOperandsQuery, Types._SearchAttributeOperandsQueryVariables>;
|
||||||
|
export const _GetAttributeChoicesDocument = gql`
|
||||||
|
query _GetAttributeChoices($slug: String!, $first: Int!, $query: String!) {
|
||||||
|
attribute(slug: $slug) {
|
||||||
|
choices(first: $first, filter: {search: $query}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
slug: id
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_GetAttributeChoicesQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_GetAttributeChoicesQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_GetAttributeChoicesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_GetAttributeChoicesQuery({
|
||||||
|
* variables: {
|
||||||
|
* slug: // value for 'slug'
|
||||||
|
* first: // value for 'first'
|
||||||
|
* query: // value for 'query'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_GetAttributeChoicesQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types._GetAttributeChoicesQuery, Types._GetAttributeChoicesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._GetAttributeChoicesQuery, Types._GetAttributeChoicesQueryVariables>(_GetAttributeChoicesDocument, options);
|
||||||
|
}
|
||||||
|
export function use_GetAttributeChoicesLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._GetAttributeChoicesQuery, Types._GetAttributeChoicesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._GetAttributeChoicesQuery, Types._GetAttributeChoicesQueryVariables>(_GetAttributeChoicesDocument, options);
|
||||||
|
}
|
||||||
|
export type _GetAttributeChoicesQueryHookResult = ReturnType<typeof use_GetAttributeChoicesQuery>;
|
||||||
|
export type _GetAttributeChoicesLazyQueryHookResult = ReturnType<typeof use_GetAttributeChoicesLazyQuery>;
|
||||||
|
export type _GetAttributeChoicesQueryResult = Apollo.QueryResult<Types._GetAttributeChoicesQuery, Types._GetAttributeChoicesQueryVariables>;
|
||||||
|
export const _GetCollectionsChoicesDocument = gql`
|
||||||
|
query _GetCollectionsChoices($first: Int!, $query: String!) {
|
||||||
|
collections(first: $first, filter: {search: $query}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_GetCollectionsChoicesQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_GetCollectionsChoicesQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_GetCollectionsChoicesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_GetCollectionsChoicesQuery({
|
||||||
|
* variables: {
|
||||||
|
* first: // value for 'first'
|
||||||
|
* query: // value for 'query'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_GetCollectionsChoicesQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types._GetCollectionsChoicesQuery, Types._GetCollectionsChoicesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._GetCollectionsChoicesQuery, Types._GetCollectionsChoicesQueryVariables>(_GetCollectionsChoicesDocument, options);
|
||||||
|
}
|
||||||
|
export function use_GetCollectionsChoicesLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._GetCollectionsChoicesQuery, Types._GetCollectionsChoicesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._GetCollectionsChoicesQuery, Types._GetCollectionsChoicesQueryVariables>(_GetCollectionsChoicesDocument, options);
|
||||||
|
}
|
||||||
|
export type _GetCollectionsChoicesQueryHookResult = ReturnType<typeof use_GetCollectionsChoicesQuery>;
|
||||||
|
export type _GetCollectionsChoicesLazyQueryHookResult = ReturnType<typeof use_GetCollectionsChoicesLazyQuery>;
|
||||||
|
export type _GetCollectionsChoicesQueryResult = Apollo.QueryResult<Types._GetCollectionsChoicesQuery, Types._GetCollectionsChoicesQueryVariables>;
|
||||||
|
export const _GetCategoriesChoicesDocument = gql`
|
||||||
|
query _GetCategoriesChoices($first: Int!, $query: String!) {
|
||||||
|
categories(first: $first, filter: {search: $query}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_GetCategoriesChoicesQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_GetCategoriesChoicesQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_GetCategoriesChoicesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_GetCategoriesChoicesQuery({
|
||||||
|
* variables: {
|
||||||
|
* first: // value for 'first'
|
||||||
|
* query: // value for 'query'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_GetCategoriesChoicesQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types._GetCategoriesChoicesQuery, Types._GetCategoriesChoicesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._GetCategoriesChoicesQuery, Types._GetCategoriesChoicesQueryVariables>(_GetCategoriesChoicesDocument, options);
|
||||||
|
}
|
||||||
|
export function use_GetCategoriesChoicesLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._GetCategoriesChoicesQuery, Types._GetCategoriesChoicesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._GetCategoriesChoicesQuery, Types._GetCategoriesChoicesQueryVariables>(_GetCategoriesChoicesDocument, options);
|
||||||
|
}
|
||||||
|
export type _GetCategoriesChoicesQueryHookResult = ReturnType<typeof use_GetCategoriesChoicesQuery>;
|
||||||
|
export type _GetCategoriesChoicesLazyQueryHookResult = ReturnType<typeof use_GetCategoriesChoicesLazyQuery>;
|
||||||
|
export type _GetCategoriesChoicesQueryResult = Apollo.QueryResult<Types._GetCategoriesChoicesQuery, Types._GetCategoriesChoicesQueryVariables>;
|
||||||
|
export const _GetProductTypesChoicesDocument = gql`
|
||||||
|
query _GetProductTypesChoices($first: Int!, $query: String!) {
|
||||||
|
productTypes(first: $first, filter: {search: $query}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __use_GetProductTypesChoicesQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `use_GetProductTypesChoicesQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `use_GetProductTypesChoicesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = use_GetProductTypesChoicesQuery({
|
||||||
|
* variables: {
|
||||||
|
* first: // value for 'first'
|
||||||
|
* query: // value for 'query'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function use_GetProductTypesChoicesQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types._GetProductTypesChoicesQuery, Types._GetProductTypesChoicesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useQuery<Types._GetProductTypesChoicesQuery, Types._GetProductTypesChoicesQueryVariables>(_GetProductTypesChoicesDocument, options);
|
||||||
|
}
|
||||||
|
export function use_GetProductTypesChoicesLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types._GetProductTypesChoicesQuery, Types._GetProductTypesChoicesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return ApolloReactHooks.useLazyQuery<Types._GetProductTypesChoicesQuery, Types._GetProductTypesChoicesQueryVariables>(_GetProductTypesChoicesDocument, options);
|
||||||
|
}
|
||||||
|
export type _GetProductTypesChoicesQueryHookResult = ReturnType<typeof use_GetProductTypesChoicesQuery>;
|
||||||
|
export type _GetProductTypesChoicesLazyQueryHookResult = ReturnType<typeof use_GetProductTypesChoicesLazyQuery>;
|
||||||
|
export type _GetProductTypesChoicesQueryResult = Apollo.QueryResult<Types._GetProductTypesChoicesQuery, Types._GetProductTypesChoicesQueryVariables>;
|
||||||
export const TriggerWebhookDryRunDocument = gql`
|
export const TriggerWebhookDryRunDocument = gql`
|
||||||
mutation TriggerWebhookDryRun($objectId: ID!, $query: String!) {
|
mutation TriggerWebhookDryRun($objectId: ID!, $query: String!) {
|
||||||
webhookDryRun(objectId: $objectId, query: $query) {
|
webhookDryRun(objectId: $objectId, query: $query) {
|
||||||
|
|
|
@ -8323,6 +8323,87 @@ export type AddressValidationRulesQueryVariables = Exact<{
|
||||||
|
|
||||||
export type AddressValidationRulesQuery = { __typename: 'Query', addressValidationRules: { __typename: 'AddressValidationData', allowedFields: Array<string>, countryAreaChoices: Array<{ __typename: 'ChoiceValue', raw: string | null, verbose: string | null }> } | null };
|
export type AddressValidationRulesQuery = { __typename: 'Query', addressValidationRules: { __typename: 'AddressValidationData', allowedFields: Array<string>, countryAreaChoices: Array<{ __typename: 'ChoiceValue', raw: string | null, verbose: string | null }> } | null };
|
||||||
|
|
||||||
|
export type _GetDynamicLeftOperandsQueryVariables = Exact<{
|
||||||
|
first: Scalars['Int'];
|
||||||
|
query: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _GetDynamicLeftOperandsQuery = { __typename: 'Query', attributes: { __typename: 'AttributeCountableConnection', edges: Array<{ __typename: 'AttributeCountableEdge', node: { __typename: 'Attribute', id: string, name: string | null, slug: string | null, inputType: AttributeInputTypeEnum | null } }> } | null };
|
||||||
|
|
||||||
|
export type _GetChannelOperandsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _GetChannelOperandsQuery = { __typename: 'Query', channels: Array<{ __typename: 'Channel', id: string, name: string, slug: string }> | null };
|
||||||
|
|
||||||
|
export type _SearchCollectionsOperandsQueryVariables = Exact<{
|
||||||
|
first: Scalars['Int'];
|
||||||
|
collectionsSlugs?: InputMaybe<Array<Scalars['String']> | Scalars['String']>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _SearchCollectionsOperandsQuery = { __typename: 'Query', search: { __typename: 'CollectionCountableConnection', edges: Array<{ __typename: 'CollectionCountableEdge', node: { __typename: 'Collection', id: string, name: string, slug: string } }> } | null };
|
||||||
|
|
||||||
|
export type _SearchCategoriesOperandsQueryVariables = Exact<{
|
||||||
|
after?: InputMaybe<Scalars['String']>;
|
||||||
|
first: Scalars['Int'];
|
||||||
|
categoriesSlugs?: InputMaybe<Array<Scalars['String']> | Scalars['String']>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _SearchCategoriesOperandsQuery = { __typename: 'Query', search: { __typename: 'CategoryCountableConnection', edges: Array<{ __typename: 'CategoryCountableEdge', node: { __typename: 'Category', id: string, name: string, slug: string } }> } | null };
|
||||||
|
|
||||||
|
export type _SearchProductTypesOperandsQueryVariables = Exact<{
|
||||||
|
after?: InputMaybe<Scalars['String']>;
|
||||||
|
first: Scalars['Int'];
|
||||||
|
productTypesSlugs?: InputMaybe<Array<Scalars['String']> | Scalars['String']>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _SearchProductTypesOperandsQuery = { __typename: 'Query', search: { __typename: 'ProductTypeCountableConnection', edges: Array<{ __typename: 'ProductTypeCountableEdge', node: { __typename: 'ProductType', id: string, name: string, slug: string } }> } | null };
|
||||||
|
|
||||||
|
export type _SearchAttributeOperandsQueryVariables = Exact<{
|
||||||
|
attributesSlugs?: InputMaybe<Array<Scalars['String']> | Scalars['String']>;
|
||||||
|
choicesIds?: InputMaybe<Array<Scalars['ID']> | Scalars['ID']>;
|
||||||
|
first: Scalars['Int'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _SearchAttributeOperandsQuery = { __typename: 'Query', search: { __typename: 'AttributeCountableConnection', edges: Array<{ __typename: 'AttributeCountableEdge', node: { __typename: 'Attribute', id: string, name: string | null, slug: string | null, inputType: AttributeInputTypeEnum | null, choices: { __typename: 'AttributeValueCountableConnection', edges: Array<{ __typename: 'AttributeValueCountableEdge', node: { __typename: 'AttributeValue', id: string, name: string | null, slug: string } }> } | null } }> } | null };
|
||||||
|
|
||||||
|
export type _GetAttributeChoicesQueryVariables = Exact<{
|
||||||
|
slug: Scalars['String'];
|
||||||
|
first: Scalars['Int'];
|
||||||
|
query: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _GetAttributeChoicesQuery = { __typename: 'Query', attribute: { __typename: 'Attribute', choices: { __typename: 'AttributeValueCountableConnection', edges: Array<{ __typename: 'AttributeValueCountableEdge', node: { __typename: 'AttributeValue', id: string, name: string | null, slug: string } }> } | null } | null };
|
||||||
|
|
||||||
|
export type _GetCollectionsChoicesQueryVariables = Exact<{
|
||||||
|
first: Scalars['Int'];
|
||||||
|
query: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _GetCollectionsChoicesQuery = { __typename: 'Query', collections: { __typename: 'CollectionCountableConnection', edges: Array<{ __typename: 'CollectionCountableEdge', node: { __typename: 'Collection', id: string, name: string, slug: string } }> } | null };
|
||||||
|
|
||||||
|
export type _GetCategoriesChoicesQueryVariables = Exact<{
|
||||||
|
first: Scalars['Int'];
|
||||||
|
query: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _GetCategoriesChoicesQuery = { __typename: 'Query', categories: { __typename: 'CategoryCountableConnection', edges: Array<{ __typename: 'CategoryCountableEdge', node: { __typename: 'Category', id: string, name: string, slug: string } }> } | null };
|
||||||
|
|
||||||
|
export type _GetProductTypesChoicesQueryVariables = Exact<{
|
||||||
|
first: Scalars['Int'];
|
||||||
|
query: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type _GetProductTypesChoicesQuery = { __typename: 'Query', productTypes: { __typename: 'ProductTypeCountableConnection', edges: Array<{ __typename: 'ProductTypeCountableEdge', node: { __typename: 'ProductType', id: string, name: string, slug: string } }> } | null };
|
||||||
|
|
||||||
export type TriggerWebhookDryRunMutationVariables = Exact<{
|
export type TriggerWebhookDryRunMutationVariables = Exact<{
|
||||||
objectId: Scalars['ID'];
|
objectId: Scalars['ID'];
|
||||||
query: Scalars['String'];
|
query: Scalars['String'];
|
||||||
|
|
Loading…
Reference in a new issue