diff --git a/.changeset/fuzzy-apes-try.md b/.changeset/fuzzy-apes-try.md new file mode 100644 index 000000000..9b7061284 --- /dev/null +++ b/.changeset/fuzzy-apes-try.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": patch +--- + +Experimental filters: fix providers issues diff --git a/src/components/ConditionalFilter/API/FilterAPIProvider.ts b/src/components/ConditionalFilter/API/FilterAPIProvider.ts index 5c0d5b2a8..0e448a05d 100644 --- a/src/components/ConditionalFilter/API/FilterAPIProvider.ts +++ b/src/components/ConditionalFilter/API/FilterAPIProvider.ts @@ -1,8 +1,6 @@ import { FilterContainer } from "../FilterElement"; import { ItemOption } from "../FilterElement/ConditionValue"; import { LeftOperand } from "../LeftOperandsProvider"; -import { FetchingParams } from "../ValueProvider/TokenArray/fetchingParams"; -import { InitialStateResponse } from "./InitialStateResponse"; export interface FilterAPIProvider { fetchRightOptions: ( @@ -11,8 +9,4 @@ export interface FilterAPIProvider { inputValue: string, ) => Promise; fetchLeftOptions: (inputValue: string) => Promise; - useInitialState: (fetchingParams: FetchingParams) => { - data: InitialStateResponse; - loading: boolean; - }; } diff --git a/src/components/ConditionalFilter/API/InitialStateResponse.ts b/src/components/ConditionalFilter/API/InitialStateResponse.ts index d58ce4fe2..496c5f4f1 100644 --- a/src/components/ConditionalFilter/API/InitialStateResponse.ts +++ b/src/components/ConditionalFilter/API/InitialStateResponse.ts @@ -31,6 +31,10 @@ export class InitialStateResponse implements InitialState { return this.attribute[name]; } + public static empty() { + return new InitialStateResponse([], {}, [], [], []); + } + public filterByUrlToken(token: UrlToken) { if (token.isAttribute()) { return this.attribute[token.name].choices.filter(({ value }) => diff --git a/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx b/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx index 4bc5a671a..0ccc5436b 100644 --- a/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx +++ b/src/components/ConditionalFilter/API/ProductFilterAPIProvider.tsx @@ -1,7 +1,6 @@ import { ApolloClient, useApolloClient } from "@apollo/client"; import { FilterContainer, FilterElement } from "../FilterElement"; -import { FetchingParams } from "../ValueProvider/TokenArray/fetchingParams"; import { FilterAPIProvider } from "./FilterAPIProvider"; import { AttributeChoicesHandler, @@ -12,11 +11,6 @@ import { Handler, ProductTypeHandler, } from "./Handler"; -import { - createInitialStateFromData, - useDataFromAPI, -} from "./initialState/helpers"; -import { InitialStateResponse } from "./InitialStateResponse"; const getFilterElement = ( value: FilterContainer, @@ -85,31 +79,8 @@ export const useProductFilterAPIProvider = (): FilterAPIProvider => { return handler.fetch(); }; - const useInitialState = (fetchingParams: FetchingParams) => { - const { data, loading } = useDataFromAPI({ - ...fetchingParams, - }); - - const initialState = createInitialStateFromData( - data, - fetchingParams.channel, - ); - - return { - data: new InitialStateResponse( - initialState.category, - initialState.attribute, - initialState.channel, - initialState.collection, - initialState.producttype, - ), - loading, - }; - }; - return { fetchRightOptions, fetchLeftOptions, - useInitialState, }; }; diff --git a/src/components/ConditionalFilter/API/initialState/helpers.ts b/src/components/ConditionalFilter/API/initialState/helpers.ts index 4864561b1..41b0c0ff9 100644 --- a/src/components/ConditionalFilter/API/initialState/helpers.ts +++ b/src/components/ConditionalFilter/API/initialState/helpers.ts @@ -1,24 +1,12 @@ -import { ApolloQueryResult, useApolloClient } from "@apollo/client"; +import { ApolloQueryResult } from "@apollo/client"; import { - _GetChannelOperandsDocument, _GetChannelOperandsQuery, - _GetChannelOperandsQueryVariables, - _SearchAttributeOperandsDocument, _SearchAttributeOperandsQuery, - _SearchAttributeOperandsQueryVariables, - _SearchCategoriesOperandsDocument, _SearchCategoriesOperandsQuery, - _SearchCategoriesOperandsQueryVariables, - _SearchCollectionsOperandsDocument, _SearchCollectionsOperandsQuery, - _SearchCollectionsOperandsQueryVariables, - _SearchProductTypesOperandsDocument, _SearchProductTypesOperandsQuery, - _SearchProductTypesOperandsQueryVariables, } from "@dashboard/graphql"; -import { useEffect, useState } from "react"; -import { FetchingParams } from "../../ValueProvider/TokenArray/fetchingParams"; import { createOptionsFromAPI } from "../Handler"; import { InitialState } from "../InitialStateResponse"; import { InitialAPIResponse } from "./types"; @@ -118,104 +106,3 @@ export const createInitialStateFromData = ( attribute: {}, }, ); - -export const useDataFromAPI = ({ - category, - collection, - producttype, - channel, - attribute, -}: FetchingParams) => { - const client = useApolloClient(); - const [data, setData] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - const queriesToRun: Array> = []; - - const fetchQueries = async () => { - const data = await Promise.all(queriesToRun); - setData(data); - setLoading(false); - }; - - if (channel.length > 0) { - queriesToRun.push( - client.query< - _GetChannelOperandsQuery, - _GetChannelOperandsQueryVariables - >({ - query: _GetChannelOperandsDocument, - }), - ); - } - - if (collection.length > 0) { - queriesToRun.push( - client.query< - _SearchCollectionsOperandsQuery, - _SearchCollectionsOperandsQueryVariables - >({ - query: _SearchCollectionsOperandsDocument, - variables: { - collectionsSlugs: collection, - first: collection.length, - }, - }), - ); - } - - if (category.length > 0) { - queriesToRun.push( - client.query< - _SearchCategoriesOperandsQuery, - _SearchCategoriesOperandsQueryVariables - >({ - query: _SearchCategoriesOperandsDocument, - variables: { - categoriesSlugs: category, - first: category.length, - }, - }), - ); - } - - if (producttype.length > 0) { - queriesToRun.push( - client.query< - _SearchProductTypesOperandsQuery, - _SearchProductTypesOperandsQueryVariables - >({ - query: _SearchProductTypesOperandsDocument, - variables: { - productTypesSlugs: producttype, - first: producttype.length, - }, - }), - ); - } - - if (Object.keys(attribute).length > 0) { - queriesToRun.push( - client.query< - _SearchAttributeOperandsQuery, - _SearchAttributeOperandsQueryVariables - >({ - query: _SearchAttributeOperandsDocument, - variables: { - attributesSlugs: Object.keys(attribute), - choicesIds: Object.values(attribute).flat(), - first: Object.keys(attribute).length, - }, - }), - ); - } - - void fetchQueries(); - }, []); - - return { - data, - loading, - }; -}; diff --git a/src/components/ConditionalFilter/API/initialState/index.ts b/src/components/ConditionalFilter/API/initialState/index.ts index 71f67f804..174987ad9 100644 --- a/src/components/ConditionalFilter/API/initialState/index.ts +++ b/src/components/ConditionalFilter/API/initialState/index.ts @@ -1 +1 @@ -export * from "./useInitalAPIState"; +export * from "./useInitialAPIState"; diff --git a/src/components/ConditionalFilter/API/initialState/useInitalAPIState.tsx b/src/components/ConditionalFilter/API/initialState/useInitalAPIState.tsx deleted file mode 100644 index 671d29c98..000000000 --- a/src/components/ConditionalFilter/API/initialState/useInitalAPIState.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { FetchingParams } from "../../ValueProvider/TokenArray/fetchingParams"; -import { InitialStateResponse } from "../InitialStateResponse"; -import { createInitialStateFromData, useDataFromAPI } from "./helpers"; - -export interface InitialStateAPIProvider { - data: InitialStateResponse; - loading: boolean; -} - -export const useInitialAPIState = ( - props: FetchingParams, -): InitialStateAPIProvider => { - const { data, loading } = useDataFromAPI({ - ...props, - }); - - const initialState = createInitialStateFromData(data, props.channel); - - return { - data: new InitialStateResponse( - initialState.category, - initialState.attribute, - initialState.channel, - initialState.collection, - initialState.producttype, - ), - loading, - }; -}; diff --git a/src/components/ConditionalFilter/API/initialState/useInitialAPIState.tsx b/src/components/ConditionalFilter/API/initialState/useInitialAPIState.tsx new file mode 100644 index 000000000..449dbafb1 --- /dev/null +++ b/src/components/ConditionalFilter/API/initialState/useInitialAPIState.tsx @@ -0,0 +1,138 @@ +import { useApolloClient } from "@apollo/client"; +import { + _GetChannelOperandsDocument, + _GetChannelOperandsQuery, + _GetChannelOperandsQueryVariables, + _SearchAttributeOperandsDocument, + _SearchAttributeOperandsQuery, + _SearchAttributeOperandsQueryVariables, + _SearchCategoriesOperandsDocument, + _SearchCategoriesOperandsQuery, + _SearchCategoriesOperandsQueryVariables, + _SearchCollectionsOperandsDocument, + _SearchCollectionsOperandsQuery, + _SearchCollectionsOperandsQueryVariables, + _SearchProductTypesOperandsDocument, + _SearchProductTypesOperandsQuery, + _SearchProductTypesOperandsQueryVariables, +} from "@dashboard/graphql"; +import { useState } from "react"; + +import { FetchingParams } from "../../ValueProvider/TokenArray/fetchingParams"; +import { InitialStateResponse } from "../InitialStateResponse"; +import { createInitialStateFromData } from "./helpers"; +import { InitialAPIResponse } from "./types"; + +export interface InitialAPIState { + data: InitialStateResponse; + loading: boolean; + fetchQueries: (params: FetchingParams) => void; +} + +export const useProductInitialAPIState = (): InitialAPIState => { + const client = useApolloClient(); + const [data, setData] = useState( + InitialStateResponse.empty(), + ); + const [loading, setLoading] = useState(true); + const queriesToRun: Array> = []; + + const fetchQueries = async ({ + category, + collection, + producttype, + channel, + attribute, + }: FetchingParams) => { + if (channel.length > 0) { + queriesToRun.push( + client.query< + _GetChannelOperandsQuery, + _GetChannelOperandsQueryVariables + >({ + query: _GetChannelOperandsDocument, + }), + ); + } + + if (collection.length > 0) { + queriesToRun.push( + client.query< + _SearchCollectionsOperandsQuery, + _SearchCollectionsOperandsQueryVariables + >({ + query: _SearchCollectionsOperandsDocument, + variables: { + collectionsSlugs: collection, + first: collection.length, + }, + }), + ); + } + + if (category.length > 0) { + queriesToRun.push( + client.query< + _SearchCategoriesOperandsQuery, + _SearchCategoriesOperandsQueryVariables + >({ + query: _SearchCategoriesOperandsDocument, + variables: { + categoriesSlugs: category, + first: category.length, + }, + }), + ); + } + + if (producttype.length > 0) { + queriesToRun.push( + client.query< + _SearchProductTypesOperandsQuery, + _SearchProductTypesOperandsQueryVariables + >({ + query: _SearchProductTypesOperandsDocument, + variables: { + productTypesSlugs: producttype, + first: producttype.length, + }, + }), + ); + } + + if (Object.keys(attribute).length > 0) { + queriesToRun.push( + client.query< + _SearchAttributeOperandsQuery, + _SearchAttributeOperandsQueryVariables + >({ + query: _SearchAttributeOperandsDocument, + variables: { + attributesSlugs: Object.keys(attribute), + choicesIds: Object.values(attribute).flat(), + first: Object.keys(attribute).length, + }, + }), + ); + } + + const data = await Promise.all(queriesToRun); + const initialState = createInitialStateFromData(data, channel); + setData( + new InitialStateResponse( + initialState.category, + initialState.attribute, + initialState.channel, + initialState.collection, + initialState.producttype, + ), + ); + setLoading(false); + }; + + return { + data, + loading, + fetchQueries, + }; +}; diff --git a/src/components/ConditionalFilter/ConditionalFilters.tsx b/src/components/ConditionalFilter/ConditionalFilters.tsx index 430be636c..c7fb105e1 100644 --- a/src/components/ConditionalFilter/ConditionalFilters.tsx +++ b/src/components/ConditionalFilter/ConditionalFilters.tsx @@ -17,10 +17,7 @@ export const ConditionalFilters: FC = () => { {valueProvider.loading ? ( Loading... ) : ( - + )} ); diff --git a/src/components/ConditionalFilter/FiltersArea.tsx b/src/components/ConditionalFilter/FiltersArea.tsx index 5ce4af1b7..fe6b7e3e2 100644 --- a/src/components/ConditionalFilter/FiltersArea.tsx +++ b/src/components/ConditionalFilter/FiltersArea.tsx @@ -6,11 +6,10 @@ import { FilterContainer } from "./FilterElement"; import { useFilterContainer } from "./useFilterContainer"; interface FiltersAreaProps { - filterValue: FilterContainer; onConfirm: (value: FilterContainer) => void; } -export const FiltersArea = ({ filterValue, onConfirm }: FiltersAreaProps) => { +export const FiltersArea = ({ onConfirm }: FiltersAreaProps) => { const { apiProvider, leftOperandsProvider } = useConditionalFilterContext(); const { @@ -22,7 +21,7 @@ export const FiltersArea = ({ filterValue, onConfirm }: FiltersAreaProps) => { updateCondition, updateRightOptions, updateLeftOptions, - } = useFilterContainer(filterValue, apiProvider, leftOperandsProvider); + } = useFilterContainer(apiProvider, leftOperandsProvider); const handleStateChange = async (event: FilterEvent["detail"]) => { if (!event) return; diff --git a/src/components/ConditionalFilter/ValueProvider/useUrlValueProvider.ts b/src/components/ConditionalFilter/ValueProvider/useUrlValueProvider.ts index 7141d500f..c87dfc6ed 100644 --- a/src/components/ConditionalFilter/ValueProvider/useUrlValueProvider.ts +++ b/src/components/ConditionalFilter/ValueProvider/useUrlValueProvider.ts @@ -1,7 +1,8 @@ import { stringify } from "qs"; +import { useEffect } from "react"; import useRouter from "use-react-router"; -import { FilterAPIProvider } from "../API/FilterAPIProvider"; +import { InitialAPIState } from "../API"; import { FilterContainer } from "../FilterElement"; import { FilterValueProvider } from "../FilterValueProvider"; import { useTokenArray } from "./TokenArray"; @@ -23,16 +24,21 @@ const prepareStructure = (filterValue: FilterContainer): Structure => }); export const useUrlValueProvider = ( - apiProvider: FilterAPIProvider, + initialState: InitialAPIState, ): FilterValueProvider => { const router = useRouter(); const params = new URLSearchParams(router.location.search); + const { data, loading, fetchQueries } = initialState; + params.delete("asc"); params.delete("sort"); const tokenizedUrl = useTokenArray(params.toString()); const fetchingParams = tokenizedUrl.getFetchingParams(); - const { data, loading } = apiProvider.useInitialState(fetchingParams); + useEffect(() => { + fetchQueries(fetchingParams); + }, []); + const value = loading ? [] : tokenizedUrl.asFilterValuesFromResponse(data); const persist = (filterValue: FilterContainer) => { diff --git a/src/components/ConditionalFilter/context/context.ts b/src/components/ConditionalFilter/context/context.ts index 36ad1ed25..d2da3efc7 100644 --- a/src/components/ConditionalFilter/context/context.ts +++ b/src/components/ConditionalFilter/context/context.ts @@ -3,9 +3,11 @@ import { createContext } from "react"; import { FilterAPIProvider } from "../API/FilterAPIProvider"; import { FilterValueProvider } from "../FilterValueProvider"; import { LeftOperandsProvider } from "../LeftOperandsProvider"; +import { useContainerState } from "../useContainerState"; export const ConditionalFilterContext = createContext<{ apiProvider: FilterAPIProvider; valueProvider: FilterValueProvider; leftOperandsProvider: LeftOperandsProvider; + containerState: ReturnType; } | null>(null); diff --git a/src/components/ConditionalFilter/context/provider.tsx b/src/components/ConditionalFilter/context/provider.tsx index 97e6895d4..aa360145d 100644 --- a/src/components/ConditionalFilter/context/provider.tsx +++ b/src/components/ConditionalFilter/context/provider.tsx @@ -1,14 +1,18 @@ import React, { FC } from "react"; +import { useProductInitialAPIState } from "../API/initialState/useInitialAPIState"; import { useProductFilterAPIProvider } from "../API/ProductFilterAPIProvider"; +import { useContainerState } from "../useContainerState"; import { useFilterLeftOperandsProvider } from "../useFilterLeftOperands"; import { useUrlValueProvider } from "../ValueProvider/useUrlValueProvider"; import { ConditionalFilterContext } from "./context"; export const ConditionalProductFilterProvider: FC = ({ children }) => { const apiProvider = useProductFilterAPIProvider(); - const valueProvider = useUrlValueProvider(apiProvider); + const initialState = useProductInitialAPIState(); + const valueProvider = useUrlValueProvider(initialState); const leftOperandsProvider = useFilterLeftOperandsProvider(); + const containerState = useContainerState(valueProvider.value); return ( { apiProvider, valueProvider, leftOperandsProvider, + containerState, }} > {children} diff --git a/src/components/ConditionalFilter/useContainerState.ts b/src/components/ConditionalFilter/useContainerState.ts index 89eee9e3e..0ff808060 100644 --- a/src/components/ConditionalFilter/useContainerState.ts +++ b/src/components/ConditionalFilter/useContainerState.ts @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { FilterContainer, FilterElement } from "./FilterElement"; @@ -8,6 +8,12 @@ type Element = FilterContainer[number]; export const useContainerState = (initialValue: FilterContainer) => { const [value, setValue] = useState(initialValue); + useEffect(() => { + if (value.length === 0 && initialValue.length > 0) { + setValue(initialValue); + } + }, [initialValue]); + const isFilterElement = ( elIndex: number, index: number, diff --git a/src/components/ConditionalFilter/useFilterContainer.ts b/src/components/ConditionalFilter/useFilterContainer.ts index 660192de8..f7ca17eca 100644 --- a/src/components/ConditionalFilter/useFilterContainer.ts +++ b/src/components/ConditionalFilter/useFilterContainer.ts @@ -1,18 +1,17 @@ import useDebounce from "@dashboard/hooks/useDebounce"; import { FilterAPIProvider } from "./API/FilterAPIProvider"; -import { FilterContainer } from "./FilterElement"; +import { useConditionalFilterContext } from "./context"; import { ConditionValue, ItemOption } from "./FilterElement/ConditionValue"; import { LeftOperandsProvider } from "./LeftOperandsProvider"; -import { useContainerState } from "./useContainerState"; export const useFilterContainer = ( - initialValue: FilterContainer, apiProvider: FilterAPIProvider, leftOperandsProvider: LeftOperandsProvider, ) => { - const { value, updateAt, removeAt, createEmpty } = - useContainerState(initialValue); + const { + containerState: { value, updateAt, removeAt, createEmpty }, + } = useConditionalFilterContext(); const addEmpty = () => { createEmpty();