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)) && (
+ (загрузка...)
+ )}
))}