From ad5dcc03e3a3ddbd519f6b7aa4d0a1d8df57e9de Mon Sep 17 00:00:00 2001 From: Bivekich Date: Sun, 13 Jul 2025 21:42:06 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BD=D0=B0=D0=B2=D0=B8=D0=B3=D0=B0=D1=86=D0=B8=D0=BE?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BA=D0=B0=D1=82=D0=B5=D0=B3=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=B9=20=D1=81=20=D0=B8=D0=BA=D0=BE=D0=BD=D0=BA?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8=20=D0=B8=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B8=D0=BA=D0=BE=D0=BD=D0=BE=D0=BA=20=D0=B2=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=B5=20?= =?UTF-8?q?BottomHead.=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D1=82=D0=B8=D0=BF=D1=8B=20=D0=B4=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D0=B8=20=D1=81=D1=82=D0=B8=D0=BB=D0=B8=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BD=D0=B0=D0=B2=D0=B8=D0=B3=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B8=D0=BA=D0=BE=D0=BD=D0=BE?= =?UTF-8?q?=D0=BA.=20=D0=9E=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=B7=D0=B0=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=B7=D0=BA=D0=B0=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=BA=D0=B0=D1=82=D0=B5=D0=B3=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=B2=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B5=20BottomHeadPartsIndex.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/BottomHead.tsx | 126 ++++++++-- src/components/BottomHeadPartsIndex.tsx | 294 +++++++++++++++--------- src/lib/graphql.ts | 19 ++ src/pages/catalog.tsx | 72 +++++- src/styles/globals.css | 19 ++ src/types/index.ts | 12 + 6 files changed, 406 insertions(+), 136 deletions(-) create mode 100644 src/types/index.ts diff --git a/src/components/BottomHead.tsx b/src/components/BottomHead.tsx index e24ca25..3238712 100644 --- a/src/components/BottomHead.tsx +++ b/src/components/BottomHead.tsx @@ -2,8 +2,9 @@ import React, { useState, useEffect } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import { useQuery } from '@apollo/client'; -import { GET_PARTSINDEX_CATEGORIES } from '@/lib/graphql'; +import { GET_PARTSINDEX_CATEGORIES, GET_NAVIGATION_CATEGORIES } from '@/lib/graphql'; import { PartsIndexCatalogsData, PartsIndexCatalogsVariables, PartsIndexCatalog } from '@/types/partsindex'; +import { NavigationCategory } from '@/types'; function useIsMobile(breakpoint = 767) { const [isMobile, setIsMobile] = React.useState(false); @@ -111,6 +112,22 @@ const transformPartsIndexToTabData = (catalogs: PartsIndexCatalog[]) => { return transformed; }; +// Функция для поиска иконки для категории +const findCategoryIcon = (catalogId: string, navigationCategories: NavigationCategory[]): string | null => { + console.log('🔍 Ищем иконку для catalogId:', catalogId); + console.log('📋 Доступные навигационные категории:', navigationCategories); + + // Ищем навигационную категорию для данного каталога (без группы) + const categoryIcon = navigationCategories.find( + nav => nav.partsIndexCatalogId === catalogId && (!nav.partsIndexGroupId || nav.partsIndexGroupId === '') + ); + + console.log('🎯 Найденная категория:', categoryIcon); + console.log('🖼️ Возвращаемая иконка:', categoryIcon?.icon || null); + + return categoryIcon?.icon || null; +}; + const BottomHead = ({ menuOpen, onClose }: { menuOpen: boolean; onClose: () => void }) => { const isMobile = useIsMobile(); const router = useRouter(); @@ -155,6 +172,20 @@ const BottomHead = ({ menuOpen, onClose }: { menuOpen: boolean; onClose: () => v } ); + // Получаем навигационные категории с иконками + const { data: navigationData, loading: navigationLoading, error: navigationError } = useQuery<{ navigationCategories: NavigationCategory[] }>( + GET_NAVIGATION_CATEGORIES, + { + errorPolicy: 'all', + onCompleted: (data) => { + console.log('🎉 Навигационные категории получены:', data); + }, + onError: (error) => { + console.error('❌ Ошибка загрузки навигационных категорий:', error); + } + } + ); + // Обновляем данные табов когда получаем данные от API useEffect(() => { if (catalogsData?.partsIndexCategoriesWithGroups && catalogsData.partsIndexCategoriesWithGroups.length > 0) { @@ -356,27 +387,80 @@ const BottomHead = ({ menuOpen, onClose }: { menuOpen: boolean; onClose: () => v
{/* Меню с иконками - показываем все категории из API */} - {tabData.map((tab, idx) => ( - { - setActiveTabIndex(idx); - }} - style={{ cursor: "pointer" }} - > -
-
- {/* SVG-звезда */} - - - + {tabData.map((tab, idx) => { + // Получаем catalogId для поиска иконки + const catalogId = catalogsData?.partsIndexCategoriesWithGroups?.[idx]?.id || `fallback_${idx}`; + console.log(`🏷️ Обрабатываем категорию ${idx}: "${tab.label}" с catalogId: "${catalogId}"`); + const icon = navigationData?.navigationCategories ? findCategoryIcon(catalogId, navigationData.navigationCategories) : null; + console.log(`🎨 Для категории "${tab.label}" будет показана ${icon ? 'иконка: ' + icon : 'звездочка (fallback)'}`); + + return ( + { + setActiveTabIndex(idx); + }} + style={{ cursor: "pointer" }} + > +
+
+ {(() => { + console.log(`🎭 Рендеринг иконки для "${tab.label}": ${icon ? 'IMG' : 'SVG'}`); + console.log(`🔍 icon значение:`, icon); + return null; + })()} + + {/* Условный рендеринг: либо IMG, либо SVG */} + {icon ? ( + {tab.label} { + console.log('✅ Иконка успешно загружена:', icon); + console.log('✅ Элемент img:', e.currentTarget); + }} + onError={(e) => { + console.log('❌ Ошибка загрузки иконки:', icon); + console.log('❌ Элемент img с ошибкой:', e.currentTarget); + }} + /> + ) : ( + /* SVG-звезда как fallback */ + + + + )} +
-
-
{tab.label}
-
- ))} +
{tab.label}
+ + ); + })}
{/* Правая часть меню с подкатегориями и картинками */}
diff --git a/src/components/BottomHeadPartsIndex.tsx b/src/components/BottomHeadPartsIndex.tsx index 4eaf02e..abb59c7 100644 --- a/src/components/BottomHeadPartsIndex.tsx +++ b/src/components/BottomHeadPartsIndex.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; -import Link from "next/link"; import { useRouter } from "next/router"; +import { useQuery, useLazyQuery } from '@apollo/client'; +import { GET_PARTSINDEX_CATEGORIES } from '@/lib/graphql'; function useIsMobile(breakpoint = 767) { const [isMobile, setIsMobile] = React.useState(false); @@ -13,35 +14,38 @@ function useIsMobile(breakpoint = 767) { return isMobile; } -// Типы для Parts Index API -interface PartsIndexCatalog { - id: string; - name: string; - image: string; -} - -interface PartsIndexEntityName { - id: string; - name: string; -} - -interface PartsIndexGroup { - id: string; - name: string; - lang: string; - image: string; - lft: number; - rgt: number; - entityNames: PartsIndexEntityName[]; - subgroups: PartsIndexGroup[]; -} - +// Типы данных interface PartsIndexTabData { label: string; heading: string; links: string[]; catalogId: string; - group?: PartsIndexGroup; + group?: any; + groupsLoaded?: boolean; // флаг что группы загружены +} + +interface PartsIndexCatalog { + id: string; + name: string; + image?: string; + groups?: PartsIndexGroup[]; +} + +interface PartsIndexGroup { + id: string; + name: string; + image?: string; + entityNames?: { id: string; name: string }[]; + subgroups?: { id: string; name: string }[]; +} + +// GraphQL типы +interface PartsIndexCatalogsData { + partsIndexCategoriesWithGroups: PartsIndexCatalog[]; +} + +interface PartsIndexCatalogsVariables { + lang?: 'ru' | 'en'; } // Fallback статичные данные @@ -51,57 +55,66 @@ const fallbackTabData: PartsIndexTabData[] = [ heading: "Детали ТО", catalogId: "parts_to", links: ["Детали ТО"], + groupsLoaded: false, }, { label: "Масла", heading: "Масла", catalogId: "oils", links: ["Масла"], + groupsLoaded: false, }, { label: "Шины", heading: "Шины", catalogId: "tyres", links: ["Шины"], + groupsLoaded: false, }, ]; -// Сервис для работы с Parts Index API -const PARTS_INDEX_API_BASE = 'https://api.parts-index.com'; -const API_KEY = 'PI-E1C0ADB7-E4A8-4960-94A0-4D9C0A074DAE'; +// Создаем базовые табы только с названиями каталогов +const createBaseTabData = (catalogs: PartsIndexCatalog[]): PartsIndexTabData[] => { + console.log('🔄 Создаем базовые табы из каталогов:', catalogs.length, 'элементов'); + + return catalogs.map(catalog => ({ + label: catalog.name, + heading: catalog.name, + links: [catalog.name], // Изначально показываем только название каталога + catalogId: catalog.id, + groupsLoaded: false, // Группы еще не загружены + })); +}; -async function fetchCatalogs(): Promise { - try { - const response = await fetch(`${PARTS_INDEX_API_BASE}/v1/catalogs?lang=ru`, { - headers: { 'Accept': 'application/json' }, - }); - if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); - const data = await response.json(); - return data.list; - } catch (error) { - console.error('Ошибка получения каталогов Parts Index:', error); - return []; - } -} - -async function fetchCatalogGroup(catalogId: string): Promise { - try { - const response = await fetch( - `${PARTS_INDEX_API_BASE}/v1/catalogs/${catalogId}/groups?lang=ru`, - { - headers: { - 'Accept': 'application/json', - 'Authorization': API_KEY, - }, +// Преобразуем данные PartsIndex в формат нашего меню с группами +const transformPartsIndexToTabData = (catalog: PartsIndexCatalog): string[] => { + console.log(`📝 Обрабатываем группы каталога: "${catalog.name}"`); + + let links: string[] = []; + + if (catalog.groups && catalog.groups.length > 0) { + // Для каждой группы проверяем есть ли подгруппы + catalog.groups.forEach(group => { + if (group.subgroups && group.subgroups.length > 0) { + // Если есть подгруппы, добавляем их названия + links.push(...group.subgroups.slice(0, 9 - links.length).map(subgroup => subgroup.name)); + } else { + // Если подгрупп нет, добавляем название самой группы + if (links.length < 9) { + links.push(group.name); + } } - ); - if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); - return await response.json(); - } catch (error) { - console.error(`Ошибка получения группы каталога ${catalogId}:`, error); - return null; + }); } -} + + // Если подкатегорий нет, показываем название категории + if (links.length === 0) { + links = [catalog.name]; + } + + console.log(`🔗 Подкатегории для "${catalog.name}":`, links); + return links.slice(0, 9); // Ограничиваем максимум 9 элементов +}; const BottomHeadPartsIndex = ({ menuOpen, onClose }: { menuOpen: boolean; onClose: () => void }) => { const isMobile = useIsMobile(); @@ -109,7 +122,7 @@ const BottomHeadPartsIndex = ({ menuOpen, onClose }: { menuOpen: boolean; onClos const [mobileCategory, setMobileCategory] = useState(null); const [tabData, setTabData] = useState(fallbackTabData); const [activeTabIndex, setActiveTabIndex] = useState(0); - const [loading, setLoading] = useState(false); + const [loadingGroups, setLoadingGroups] = useState>(new Set()); // Пагинация категорий const [currentPage, setCurrentPage] = useState(0); @@ -126,52 +139,116 @@ const BottomHeadPartsIndex = ({ menuOpen, onClose }: { menuOpen: boolean; onClos } }, [menuOpen]); - // Загрузка каталогов и их групп + // Получаем только каталоги PartsIndex (без групп для начальной загрузки) + const { data: catalogsData, loading, error } = useQuery( + GET_PARTSINDEX_CATEGORIES, + { + variables: { + lang: 'ru' + }, + errorPolicy: 'all', + fetchPolicy: 'cache-first', // Используем кэширование агрессивно + nextFetchPolicy: 'cache-first', // Продолжаем использовать кэш + notifyOnNetworkStatusChange: false, + onCompleted: (data) => { + console.log('🎉 PartsIndex каталоги получены через GraphQL (базовые):', data); + }, + onError: (error) => { + console.error('❌ Ошибка загрузки PartsIndex каталогов:', error); + } + } + ); + + // Ленивый запрос для загрузки групп конкретного каталога + const [loadCatalogGroups, { loading: groupsLoading }] = useLazyQuery( + GET_PARTSINDEX_CATEGORIES, + { + errorPolicy: 'all', + fetchPolicy: 'cache-first', + nextFetchPolicy: 'cache-first', + notifyOnNetworkStatusChange: false, + onCompleted: (data) => { + console.log('🎉 Группы каталога загружены:', data); + }, + onError: (error) => { + console.error('❌ Ошибка загрузки групп каталога:', error); + } + } + ); + + // Обновляем базовые данные табов когда получаем каталоги useEffect(() => { - const loadData = async () => { - if (tabData === fallbackTabData) { // Загружаем только если еще не загружали - setLoading(true); - try { - console.log('🔄 Загружаем каталоги Parts Index...'); - const catalogs = await fetchCatalogs(); + if (catalogsData?.partsIndexCategoriesWithGroups && catalogsData.partsIndexCategoriesWithGroups.length > 0) { + console.log('✅ Обновляем базовое меню PartsIndex:', catalogsData.partsIndexCategoriesWithGroups.length, 'каталогов'); + + const baseTabData = createBaseTabData(catalogsData.partsIndexCategoriesWithGroups); + setTabData(baseTabData); + setActiveTabIndex(0); + } else if (error) { + console.warn('⚠️ Используем fallback данные из-за ошибки PartsIndex:', error); + setTabData(fallbackTabData); + setActiveTabIndex(0); + } + }, [catalogsData, error]); + + // Функция для ленивой загрузки групп при наведении на таб + const loadGroupsForTab = async (tabIndex: number) => { + const tab = tabData[tabIndex]; + if (!tab || tab.groupsLoaded || loadingGroups.has(tabIndex)) { + return; // Группы уже загружены или загружаются + } + + console.log('🔄 Загружаем группы для каталога:', tab.catalogId); + setLoadingGroups(prev => new Set([...prev, tabIndex])); + + try { + const result = await loadCatalogGroups({ + variables: { + lang: 'ru' + } + }); + + if (result.data?.partsIndexCategoriesWithGroups) { + const catalog = result.data.partsIndexCategoriesWithGroups.find(c => c.id === tab.catalogId); + if (catalog) { + const links = transformPartsIndexToTabData(catalog); - if (catalogs.length > 0) { - console.log(`✅ Получено ${catalogs.length} каталогов`); - - // Загружаем группы для первых нескольких каталогов - const catalogsToLoad = catalogs.slice(0, 10); - const tabDataPromises = catalogsToLoad.map(async (catalog) => { - const group = await fetchCatalogGroup(catalog.id); - - // Получаем подкатегории из entityNames или повторяем название категории - const links = group?.entityNames && group.entityNames.length > 0 - ? group.entityNames.slice(0, 9).map(entity => entity.name) - : [catalog.name]; // Если нет подкатегорий, повторяем название категории - - return { - label: catalog.name, - heading: catalog.name, - links, - catalogId: catalog.id, - group - }; - }); - - const apiTabData = await Promise.all(tabDataPromises); - console.log('✅ Данные обновлены:', apiTabData.length, 'категорий'); - setTabData(apiTabData as PartsIndexTabData[]); - setActiveTabIndex(0); - } - } catch (error) { - console.error('Ошибка загрузки данных Parts Index:', error); - } finally { - setLoading(false); + // Обновляем конкретный таб с загруженными группами + setTabData(prevTabs => { + const newTabs = [...prevTabs]; + newTabs[tabIndex] = { + ...newTabs[tabIndex], + links, + group: catalog.groups?.[0], + groupsLoaded: true + }; + return newTabs; + }); } } - }; + } catch (error) { + console.error('Ошибка загрузки групп для каталога:', tab.catalogId, error); + } finally { + setLoadingGroups(prev => { + const newSet = new Set(prev); + newSet.delete(tabIndex); + return newSet; + }); + } + }; - loadData(); - }, []); + // Обработчик наведения на таб - загружаем группы + const handleTabHover = (tabIndex: number) => { + loadGroupsForTab(tabIndex); + }; + + // Обработчик клика на таб + const handleTabClick = (tabIndex: number) => { + setActiveTabIndex(tabIndex); + + // Загружаем группы если еще не загружены + loadGroupsForTab(tabIndex); + }; // Обработка клика по категории для перехода в каталог const handleCategoryClick = (catalogId: string, categoryName: string, entityId?: string) => { @@ -184,7 +261,7 @@ const BottomHeadPartsIndex = ({ menuOpen, onClose }: { menuOpen: boolean; onClos query: { partsIndexCatalog: catalogId, categoryName: encodeURIComponent(categoryName), - ...(entityId && { entityId }) + ...(entityId && { partsIndexCategory: entityId }) } }); }; @@ -294,6 +371,12 @@ const BottomHeadPartsIndex = ({ menuOpen, onClose }: { menuOpen: boolean; onClos className="mobile-subcategory" key={cat.catalogId} onClick={() => { + // Загружаем группы для категории если нужно + const catIndex = tabData.findIndex(tab => tab.catalogId === cat.catalogId); + if (catIndex !== -1) { + loadGroupsForTab(catIndex); + } + const categoryWithData = { ...cat, catalogId: cat.catalogId, @@ -304,6 +387,9 @@ const BottomHeadPartsIndex = ({ menuOpen, onClose }: { menuOpen: boolean; onClos style={{ cursor: "pointer" }} > {cat.label} + {loadingGroups.has(tabData.findIndex(tab => tab.catalogId === cat.catalogId)) && ( + (загрузка...) + )}
))}
@@ -367,9 +453,8 @@ const BottomHeadPartsIndex = ({ menuOpen, onClose }: { menuOpen: boolean; onClos href="#" className={`link-block-7 w-inline-block${activeTabIndex === idx ? " w--current" : ""}`} key={tab.catalogId} - onClick={() => { - setActiveTabIndex(idx); - }} + onClick={() => handleTabClick(idx)} + onMouseEnter={() => handleTabHover(idx)} style={{ cursor: "pointer" }} >
@@ -388,6 +473,7 @@ const BottomHeadPartsIndex = ({ menuOpen, onClose }: { menuOpen: boolean; onClos

{currentPageCategories[activeTabIndex]?.heading || currentPageCategories[0]?.heading} {loading && (обновление...)} + {loadingGroups.has(activeTabIndex) && (загрузка групп...)}

diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index a4b76fe..ccf37fe 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -373,6 +373,8 @@ export const CREATE_PAYMENT = gql` } ` + + export const GET_ORDERS = gql` query GetOrders($clientId: String, $status: OrderStatus, $search: String, $limit: Int, $offset: Int) { orders(clientId: $clientId, status: $status, search: $search, limit: $limit, offset: $offset) { @@ -1367,6 +1369,23 @@ export const GET_PARTSINDEX_CATEGORIES = gql` } `; +// Навигационные категории с иконками +export const GET_NAVIGATION_CATEGORIES = gql` + query GetNavigationCategories { + navigationCategories { + id + partsIndexCatalogId + partsIndexGroupId + name + catalogName + groupName + icon + sortOrder + isHidden + } + } +`; + // Новый запрос для получения товаров каталога PartsIndex export const GET_PARTSINDEX_CATALOG_ENTITIES = gql` query GetPartsIndexCatalogEntities( diff --git a/src/pages/catalog.tsx b/src/pages/catalog.tsx index eadd4fb..2687cf6 100644 --- a/src/pages/catalog.tsx +++ b/src/pages/catalog.tsx @@ -138,7 +138,7 @@ export default function Catalog() { limit: ITEMS_PER_PAGE, page: partsIndexPage, q: searchQuery || undefined, - params: Object.keys(selectedFilters).length > 0 ? JSON.stringify(selectedFilters) : undefined + params: undefined // Будем обновлять через refetch }, skip: !isPartsIndexMode || !groupId, // Пропускаем запрос если нет groupId fetchPolicy: 'cache-and-network' @@ -146,7 +146,7 @@ export default function Catalog() { ); // Загружаем параметры фильтрации для PartsIndex - const { data: paramsData, loading: paramsLoading, error: paramsError } = useQuery( + const { data: paramsData, loading: paramsLoading, error: paramsError, refetch: refetchParams } = useQuery( GET_PARTSINDEX_CATALOG_PARAMS, { variables: { @@ -154,7 +154,7 @@ export default function Catalog() { groupId: groupId as string, lang: 'ru', q: searchQuery || undefined, - params: Object.keys(selectedFilters).length > 0 ? JSON.stringify(selectedFilters) : undefined + params: undefined // Будем обновлять через refetch }, skip: !isPartsIndexMode || !groupId, // Пропускаем запрос если нет groupId fetchPolicy: 'cache-first' @@ -215,18 +215,44 @@ export default function Catalog() { } }, [entitiesData]); + // Преобразование выбранных фильтров в формат PartsIndex API + const convertFiltersToPartsIndexParams = useCallback((): 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 на основе параметров API const generatePartsIndexFilters = useCallback((): FilterConfig[] => { if (!paramsData?.partsIndexCatalogParams?.list) { return []; } - return paramsData.partsIndexCatalogParams.list.map(param => { + return paramsData.partsIndexCatalogParams.list.map((param: any) => { if (param.type === 'range') { // Для range фильтров ищем min и max значения const numericValues = param.values - .map(v => parseFloat(v.value)) - .filter(v => !isNaN(v)); + .map((v: any) => parseFloat(v.value)) + .filter((v: number) => !isNaN(v)); const min = numericValues.length > 0 ? Math.min(...numericValues) : 0; const max = numericValues.length > 0 ? Math.max(...numericValues) : 100; @@ -243,8 +269,8 @@ export default function Catalog() { type: 'dropdown' as const, title: param.name, options: param.values - .filter(value => value.available) // Показываем только доступные - .map(value => value.title || value.value), + .filter((value: any) => value.available) // Показываем только доступные + .map((value: any) => value.title || value.value), multi: true, showAll: true }; @@ -252,6 +278,8 @@ export default function Catalog() { }); }, [paramsData]); + + useEffect(() => { if (isPartsIndexMode) { // Для PartsIndex генерируем фильтры на основе параметров API @@ -426,16 +454,38 @@ export default function Catalog() { // При изменении поиска или фильтров сбрасываем пагинацию setShowEmptyState(false); - // Если изменился поисковый запрос, нужно перезагрузить данные с сервера + // Если изменился поисковый запрос или фильтры, нужно перезагрузить данные с сервера if (searchQuery.trim() || Object.keys(selectedFilters).length > 0) { console.log('🔍 Поисковый запрос или фильтры изменились, сбрасываем пагинацию'); setPartsIndexPage(1); setHasMoreEntities(true); - // refetch будет автоматически вызван при изменении partsIndexPage + + // Перезагружаем данные с новыми параметрами фильтрации + const apiParams = convertFiltersToPartsIndexParams(); + const paramsString = Object.keys(apiParams).length > 0 ? JSON.stringify(apiParams) : undefined; + + // Также обновляем параметры фильтрации + refetchParams({ + catalogId: catalogId as string, + groupId: groupId as string, + lang: 'ru', + q: searchQuery || undefined, + params: paramsString + }); + + refetchEntities({ + catalogId: catalogId as string, + groupId: groupId as string, + lang: 'ru', + limit: ITEMS_PER_PAGE, + page: 1, + q: searchQuery || undefined, + params: paramsString + }); } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isPartsIndexMode, searchQuery, JSON.stringify(selectedFilters)]); + }, [isPartsIndexMode, searchQuery, JSON.stringify(selectedFilters), refetchEntities, refetchParams, convertFiltersToPartsIndexParams]); // Управляем показом пустого состояния с задержкой useEffect(() => { diff --git a/src/styles/globals.css b/src/styles/globals.css index 9593366..e3b66d6 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -377,4 +377,23 @@ button, .tooltip-title { font-size: 15px; } +} + +/* Стили для навигационных иконок */ +.navigation-icon-img { + display: block !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + z-index: 9999 !important; + max-width: 21px !important; + max-height: 20px !important; + width: 21px !important; + height: 20px !important; + object-fit: contain !important; + opacity: 1 !important; + visibility: visible !important; + border: 2px solid red !important; /* Временно для отладки */ + background-color: yellow !important; /* Временно для отладки */ + pointer-events: none !important; /* Чтобы не блокировать клики */ } \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..9007615 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,12 @@ +// Навигационные категории +export interface NavigationCategory { + id: string + partsIndexCatalogId: string + partsIndexGroupId: string | null + name: string + catalogName: string + groupName: string | null + icon: string | null + sortOrder: number + isHidden: boolean +} \ No newline at end of file