diff --git a/src/pages/catalog.tsx b/src/pages/catalog.tsx index 86cf0d6..3de49db 100644 --- a/src/pages/catalog.tsx +++ b/src/pages/catalog.tsx @@ -167,49 +167,7 @@ export default function Catalog() { const allArticles = articlesData?.partsAPIArticles || []; - // Сначала загружаем параметры фильтрации для PartsIndex - const { data: paramsData, loading: paramsLoading, error: paramsError } = useQuery( - GET_PARTSINDEX_CATALOG_PARAMS, - { - variables: { - catalogId: catalogId as string, - groupId: groupId as string, - lang: 'ru', - q: searchQuery || undefined, - params: undefined // Параметры для получения самих фильтров - }, - skip: !isPartsIndexMode || !groupId, // Пропускаем запрос если нет groupId - fetchPolicy: 'cache-first' - } - ); - - // Преобразование выбранных фильтров в строку параметров для GraphQL (стабильное мемоизирование) - const apiParamsString = useMemo((): string | undefined => { - if (!paramsData?.partsIndexCatalogParams?.list || Object.keys(selectedFilters).length === 0) { - return undefined; - } - - const apiParams: Record = {}; - - paramsData.partsIndexCatalogParams.list.forEach((param: any) => { - const selectedValues = selectedFilters[param.name]; - if (selectedValues && selectedValues.length > 0) { - // Находим соответствующие значения из API данных - const matchingValues = param.values.filter((value: any) => - selectedValues.includes(value.title || value.value) - ); - - if (matchingValues.length > 0) { - // Используем ID параметра из API и значения - apiParams[param.id] = matchingValues.map((v: any) => v.value); - } - } - }); - - return Object.keys(apiParams).length > 0 ? JSON.stringify(apiParams) : undefined; - }, [paramsData, selectedFilters]); - - // Теперь загружаем товары PartsIndex с реактивными переменными (включая фильтры) + // Загружаем товары PartsIndex const { data: entitiesData, loading: entitiesLoading, error: entitiesError, refetch: refetchEntities } = useQuery( GET_PARTSINDEX_CATALOG_ENTITIES, { @@ -220,13 +178,29 @@ export default function Catalog() { limit: PARTSINDEX_PAGE_SIZE, page: partsIndexPage, q: searchQuery || undefined, - params: apiParamsString // ✅ Теперь реактивно отслеживает изменения фильтров + params: undefined // Будем обновлять через refetch }, skip: !isPartsIndexMode || !groupId, // Пропускаем запрос если нет groupId fetchPolicy: 'cache-and-network' } ); + // Загружаем параметры фильтрации для PartsIndex + const { data: paramsData, loading: paramsLoading, error: paramsError, refetch: refetchParams } = useQuery( + GET_PARTSINDEX_CATALOG_PARAMS, + { + variables: { + catalogId: catalogId as string, + groupId: groupId as string, + lang: 'ru', + q: searchQuery || undefined, + params: undefined // Будем обновлять через refetch + }, + skip: !isPartsIndexMode || !groupId, // Пропускаем запрос если нет groupId + fetchPolicy: 'cache-first' + } + ); + // allEntities больше не используется - используем allLoadedEntities // Хук для загрузки цен товаров PartsIndex @@ -316,6 +290,32 @@ export default function Catalog() { } }, [entitiesData, isFilterChanging]); + // Преобразование выбранных фильтров в формат PartsIndex API + const convertFiltersToPartsIndexParams = useMemo((): Record => { + if (!paramsData?.partsIndexCatalogParams?.list || Object.keys(selectedFilters).length === 0) { + return {}; + } + + const apiParams: Record = {}; + + paramsData.partsIndexCatalogParams.list.forEach((param: any) => { + const selectedValues = selectedFilters[param.name]; + if (selectedValues && selectedValues.length > 0) { + // Находим соответствующие значения из API данных + const matchingValues = param.values.filter((value: any) => + selectedValues.includes(value.title || value.value) + ); + + if (matchingValues.length > 0) { + // Используем ID параметра из API и значения + apiParams[param.id] = matchingValues.map((v: any) => v.value); + } + } + }); + + return apiParams; + }, [paramsData, selectedFilters]); + // Функция автоматической подгрузки дополнительных страниц PartsIndex const autoLoadMoreEntities = useCallback(async () => { if (isAutoLoading || !hasMoreEntities || !isPartsIndexMode) { @@ -363,10 +363,10 @@ export default function Catalog() { setIsAutoLoading(true); try { - console.log('🔄 Автоподгрузка: загружаем следующую страницу PartsIndex...'); - - // Используем уже вычисленную строку параметров - const paramsString = apiParamsString; + console.log('🔄 Автоподгрузка: загружаем следующую страницу PartsIndex...'); + + const apiParams = convertFiltersToPartsIndexParams; + const paramsString = Object.keys(apiParams).length > 0 ? JSON.stringify(apiParams) : undefined; const result = await refetchEntities({ catalogId: catalogId as string, @@ -743,25 +743,91 @@ export default function Catalog() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isPartsAPIMode, searchQuery, JSON.stringify(selectedFilters), filteredArticles.length]); - // Простая логика сброса при изменении фильтров - убираем сложные refetch + // Обновляем видимые товары при изменении поиска или фильтров для PartsIndex useEffect(() => { if (isPartsIndexMode) { - console.log('🔄 Фильтры изменились, сбрасываем локальное состояние'); - - // Сбрасываем локальное состояние при изменении фильтров - setPartsIndexPage(1); - setCurrentUserPage(1); - setAccumulatedEntities([]); - setEntitiesWithOffers([]); - setVisibleEntities([]); - setEntitiesCache(new Map()); + // При изменении поиска или фильтров сбрасываем пагинацию setShowEmptyState(false); - setIsFilterChanging(true); - // Apollo автоматически перезагрузит данные при изменении переменных + // Если изменился поисковый запрос или фильтры, нужно перезагрузить данные с сервера + if (searchQuery.trim() || Object.keys(selectedFilters).length > 0) { + console.log('🔍 Поисковый запрос или фильтры изменились, сбрасываем пагинацию'); + + // Устанавливаем флаг изменения фильтров + setIsFilterChanging(true); + + setPartsIndexPage(1); + setCurrentUserPage(1); + setHasMoreEntities(true); + setAccumulatedEntities([]); + setEntitiesWithOffers([]); + setEntitiesCache(new Map()); + + // Вычисляем параметры фильтрации прямо здесь, чтобы избежать зависимости от useMemo + let apiParams: Record = {}; + if (paramsData?.partsIndexCatalogParams?.list && Object.keys(selectedFilters).length > 0) { + paramsData.partsIndexCatalogParams.list.forEach((param: any) => { + const selectedValues = selectedFilters[param.name]; + if (selectedValues && selectedValues.length > 0) { + // Находим соответствующие значения из API данных + const matchingValues = param.values.filter((value: any) => + selectedValues.includes(value.title || value.value) + ); + + if (matchingValues.length > 0) { + // Используем ID параметра из API и значения + apiParams[param.id] = matchingValues.map((v: any) => v.value); + } + } + }); + } + + const paramsString = Object.keys(apiParams).length > 0 ? JSON.stringify(apiParams) : undefined; + + console.log('🔄 Запуск refetch с новыми фильтрами:', { + searchQuery, + selectedFilters, + apiParams, + paramsString, + catalogId, + groupId + }); + + // Также обновляем параметры фильтрации + refetchParams({ + catalogId: catalogId as string, + groupId: groupId as string, + lang: 'ru', + q: searchQuery || undefined, + params: paramsString + }).then(result => { + console.log('✅ refetchParams результат:', result); + }).catch(error => { + console.error('❌ refetchParams ошибка:', error); + }); + + refetchEntities({ + catalogId: catalogId as string, + groupId: groupId as string, + lang: 'ru', + limit: PARTSINDEX_PAGE_SIZE, + page: 1, + q: searchQuery || undefined, + params: paramsString + }).then(result => { + console.log('✅ refetchEntities результат:', result.data?.partsIndexCatalogEntities?.list?.length || 0, 'товаров'); + }).catch(error => { + console.error('❌ refetchEntities ошибка:', error); + }); + } else { + // Если нет активных фильтров, сбрасываем флаг + if (isFilterChanging) { + setIsFilterChanging(false); + } + } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isPartsIndexMode, searchQuery, JSON.stringify(selectedFilters)]); + }, [isPartsIndexMode, searchQuery, JSON.stringify(selectedFilters), paramsData]); // Управляем показом пустого состояния с задержкой useEffect(() => {