Добавлено получение баннеров для главного слайдера с использованием GraphQL. Обновлен компонент HeroSlider для отображения активных баннеров с сортировкой. Реализована логика отображения дефолтного баннера при отсутствии данных. Обновлены стили и структура компонента для улучшения пользовательского интерфейса.
This commit is contained in:
@ -38,7 +38,8 @@ const mockData = Array(12).fill({
|
||||
brand: "Borsehung",
|
||||
});
|
||||
|
||||
const ITEMS_PER_PAGE = 20;
|
||||
const ITEMS_PER_PAGE = 50; // Целевое количество товаров на странице
|
||||
const PARTSINDEX_PAGE_SIZE = 25; // Размер страницы PartsIndex API (фиксированный)
|
||||
const MAX_BRANDS_DISPLAY = 10; // Сколько брендов показывать изначально
|
||||
|
||||
export default function Catalog() {
|
||||
@ -72,6 +73,13 @@ export default function Catalog() {
|
||||
const [showEmptyState, setShowEmptyState] = useState(false);
|
||||
const [partsIndexPage, setPartsIndexPage] = useState(1); // Текущая страница для PartsIndex
|
||||
const [totalPages, setTotalPages] = useState(1); // Общее количество страниц
|
||||
|
||||
// Новые состояния для логики автоподгрузки PartsIndex
|
||||
const [accumulatedEntities, setAccumulatedEntities] = useState<PartsIndexEntity[]>([]); // Все накопленные товары
|
||||
const [entitiesWithOffers, setEntitiesWithOffers] = useState<PartsIndexEntity[]>([]); // Товары с предложениями
|
||||
const [isAutoLoading, setIsAutoLoading] = useState(false); // Автоматическая подгрузка в процессе
|
||||
const [currentUserPage, setCurrentUserPage] = useState(1); // Текущая пользовательская страница
|
||||
const [entitiesCache, setEntitiesCache] = useState<Map<number, PartsIndexEntity[]>>(new Map()); // Кэш страниц
|
||||
|
||||
// Карта видимости товаров по индексу
|
||||
const [visibilityMap, setVisibilityMap] = useState<Map<number, boolean>>(new Map());
|
||||
@ -108,7 +116,8 @@ export default function Catalog() {
|
||||
categoryName,
|
||||
isPartsAPIMode,
|
||||
isPartsIndexMode,
|
||||
isPartsIndexCatalogOnly
|
||||
isPartsIndexCatalogOnly,
|
||||
'router.query': router.query
|
||||
});
|
||||
|
||||
// Загружаем артикулы PartsAPI
|
||||
@ -135,7 +144,7 @@ export default function Catalog() {
|
||||
catalogId: catalogId as string,
|
||||
groupId: groupId as string,
|
||||
lang: 'ru',
|
||||
limit: ITEMS_PER_PAGE,
|
||||
limit: PARTSINDEX_PAGE_SIZE,
|
||||
page: partsIndexPage,
|
||||
q: searchQuery || undefined,
|
||||
params: undefined // Будем обновлять через refetch
|
||||
@ -164,12 +173,24 @@ export default function Catalog() {
|
||||
// allEntities больше не используется - используем allLoadedEntities
|
||||
|
||||
// Хук для загрузки цен товаров PartsIndex
|
||||
const productsForPrices = visibleEntities.map(entity => ({
|
||||
id: entity.id,
|
||||
code: entity.code,
|
||||
brand: entity.brand.name
|
||||
}));
|
||||
const { getPrice, isLoadingPrice, loadPriceOnDemand } = useProductPrices(productsForPrices);
|
||||
const { getPrice, isLoadingPrice, ensurePriceLoaded } = useProductPrices();
|
||||
|
||||
// Загружаем цены для видимых товаров PartsIndex
|
||||
useEffect(() => {
|
||||
if (isPartsIndexMode && visibleEntities.length > 0) {
|
||||
visibleEntities.forEach((entity, index) => {
|
||||
const productForPrice = {
|
||||
id: entity.id,
|
||||
code: entity.code,
|
||||
brand: entity.brand.name
|
||||
};
|
||||
// Загружаем с небольшой задержкой чтобы не перегружать сервер
|
||||
setTimeout(() => {
|
||||
ensurePriceLoaded(productForPrice);
|
||||
}, index * 50);
|
||||
});
|
||||
}
|
||||
}, [isPartsIndexMode, visibleEntities, ensurePriceLoaded]);
|
||||
|
||||
useEffect(() => {
|
||||
if (articlesData?.partsAPIArticles) {
|
||||
@ -193,9 +214,6 @@ export default function Catalog() {
|
||||
const newEntities = entitiesData.partsIndexCatalogEntities.list;
|
||||
const pagination = entitiesData.partsIndexCatalogEntities.pagination;
|
||||
|
||||
// Обновляем список товаров
|
||||
setVisibleEntities(newEntities);
|
||||
|
||||
// Обновляем информацию о пагинации
|
||||
const currentPage = pagination?.page?.current || 1;
|
||||
const hasNext = pagination?.page?.next !== null;
|
||||
@ -204,6 +222,20 @@ export default function Catalog() {
|
||||
setPartsIndexPage(currentPage);
|
||||
setHasMoreEntities(hasNext);
|
||||
|
||||
// Сохраняем в кэш
|
||||
setEntitiesCache(prev => new Map(prev).set(currentPage, newEntities));
|
||||
|
||||
// Если это первая страница или сброс, заменяем накопленные товары
|
||||
if (currentPage === 1) {
|
||||
setAccumulatedEntities(newEntities);
|
||||
// Устанавливаем visibleEntities сразу, не дожидаясь проверки цен
|
||||
setVisibleEntities(newEntities);
|
||||
console.log('✅ Установлены visibleEntities для первой страницы:', newEntities.length);
|
||||
} else {
|
||||
// Добавляем к накопленным товарам
|
||||
setAccumulatedEntities(prev => [...prev, ...newEntities]);
|
||||
}
|
||||
|
||||
// Вычисляем общее количество страниц (приблизительно)
|
||||
if (hasNext) {
|
||||
setTotalPages(currentPage + 1); // Минимум еще одна страница
|
||||
@ -216,7 +248,7 @@ export default function Catalog() {
|
||||
}, [entitiesData]);
|
||||
|
||||
// Преобразование выбранных фильтров в формат PartsIndex API
|
||||
const convertFiltersToPartsIndexParams = useCallback((): Record<string, any> => {
|
||||
const convertFiltersToPartsIndexParams = useMemo((): Record<string, any> => {
|
||||
if (!paramsData?.partsIndexCatalogParams?.list || Object.keys(selectedFilters).length === 0) {
|
||||
return {};
|
||||
}
|
||||
@ -241,6 +273,84 @@ export default function Catalog() {
|
||||
return apiParams;
|
||||
}, [paramsData, selectedFilters]);
|
||||
|
||||
// Функция автоматической подгрузки дополнительных страниц PartsIndex
|
||||
const autoLoadMoreEntities = useCallback(async () => {
|
||||
if (isAutoLoading || !hasMoreEntities || !isPartsIndexMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('🔄 Автоподгрузка: проверяем товары с предложениями...');
|
||||
|
||||
// Восстанавливаем автоподгрузку
|
||||
console.log('🔄 Автоподгрузка активна');
|
||||
|
||||
// Подсчитываем текущее количество товаров с предложениями
|
||||
const currentEntitiesWithOffers = accumulatedEntities.filter(entity => {
|
||||
const productForPrice = { id: entity.id, code: entity.code, brand: entity.brand.name };
|
||||
const priceData = getPrice(productForPrice);
|
||||
const isLoadingPriceData = isLoadingPrice(productForPrice);
|
||||
// Товар считается "с предложениями" если у него есть реальная цена (не null и не undefined)
|
||||
return (priceData && priceData.price && priceData.price > 0) || isLoadingPriceData;
|
||||
});
|
||||
|
||||
console.log('📊 Автоподгрузка: текущее состояние:', {
|
||||
накопленоТоваров: accumulatedEntities.length,
|
||||
сПредложениями: currentEntitiesWithOffers.length,
|
||||
целевоеКоличество: ITEMS_PER_PAGE,
|
||||
естьЕщеТовары: hasMoreEntities
|
||||
});
|
||||
|
||||
// Если у нас уже достаточно товаров с предложениями, не загружаем
|
||||
if (currentEntitiesWithOffers.length >= ITEMS_PER_PAGE) {
|
||||
console.log('✅ Автоподгрузка: достаточно товаров с предложениями');
|
||||
return;
|
||||
}
|
||||
|
||||
// Даем время на загрузку цен товаров, если их слишком много загружается
|
||||
const loadingCount = accumulatedEntities.filter(entity => {
|
||||
const productForPrice = { id: entity.id, code: entity.code, brand: entity.brand.name };
|
||||
return isLoadingPrice(productForPrice);
|
||||
}).length;
|
||||
|
||||
// Ждем только если загружается больше 5 товаров одновременно
|
||||
if (loadingCount > 5) {
|
||||
console.log('⏳ Автоподгрузка: ждем загрузки цен для', loadingCount, 'товаров (больше 5)');
|
||||
return;
|
||||
}
|
||||
|
||||
// Если накопили уже много товаров, но мало с предложениями - прекращаем попытки
|
||||
if (accumulatedEntities.length >= ITEMS_PER_PAGE * 8) { // Увеличили лимит с 4 до 8 страниц
|
||||
console.log('⚠️ Автоподгрузка: достигли лимита попыток, прекращаем');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsAutoLoading(true);
|
||||
|
||||
try {
|
||||
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,
|
||||
groupId: groupId as string,
|
||||
lang: 'ru',
|
||||
limit: PARTSINDEX_PAGE_SIZE,
|
||||
page: partsIndexPage + 1,
|
||||
q: searchQuery || undefined,
|
||||
params: paramsString
|
||||
});
|
||||
|
||||
console.log('✅ Автоподгрузка: страница загружена, результат:', result.data?.partsIndexCatalogEntities?.list?.length || 0);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Автоподгрузка: ошибка загрузки следующей страницы:', error);
|
||||
} finally {
|
||||
setIsAutoLoading(false);
|
||||
}
|
||||
}, [isAutoLoading, hasMoreEntities, isPartsIndexMode, accumulatedEntities.length, partsIndexPage, refetchEntities, catalogId, groupId, searchQuery]);
|
||||
|
||||
// Генерация фильтров для PartsIndex на основе параметров API
|
||||
const generatePartsIndexFilters = useCallback((): FilterConfig[] => {
|
||||
if (!paramsData?.partsIndexCatalogParams?.list) {
|
||||
@ -292,6 +402,114 @@ export default function Catalog() {
|
||||
}
|
||||
}, [isPartsIndexMode, generatePartsIndexFilters, paramsLoading]);
|
||||
|
||||
// Автоматическая подгрузка товаров с задержкой для загрузки цен
|
||||
useEffect(() => {
|
||||
if (!isPartsIndexMode || accumulatedEntities.length === 0 || isAutoLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Даем время на загрузку цен (3 секунды после последнего изменения)
|
||||
const timer = setTimeout(() => {
|
||||
autoLoadMoreEntities();
|
||||
}, 3000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [isPartsIndexMode, accumulatedEntities.length, isAutoLoading]);
|
||||
|
||||
// Дополнительный триггер автоподгрузки при изменении количества товаров с предложениями
|
||||
useEffect(() => {
|
||||
console.log('🔍 Проверка триггера автоподгрузки:', {
|
||||
isPartsIndexMode,
|
||||
entitiesWithOffersLength: entitiesWithOffers.length,
|
||||
isAutoLoading,
|
||||
hasMoreEntities,
|
||||
targetItemsPerPage: ITEMS_PER_PAGE
|
||||
});
|
||||
|
||||
if (!isPartsIndexMode || entitiesWithOffers.length === 0 || isAutoLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Если товаров с предложениями мало, запускаем автоподгрузку через 1 секунду
|
||||
if (entitiesWithOffers.length < ITEMS_PER_PAGE && hasMoreEntities) {
|
||||
console.log('🚀 Запускаем автоподгрузку: товаров', entitiesWithOffers.length, 'из', ITEMS_PER_PAGE);
|
||||
const timer = setTimeout(() => {
|
||||
console.log('🚀 Дополнительная автоподгрузка: недостаточно товаров с предложениями');
|
||||
autoLoadMoreEntities();
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
} else {
|
||||
console.log('✅ Автоподгрузка не нужна: товаров достаточно или нет больше данных');
|
||||
}
|
||||
}, [isPartsIndexMode, entitiesWithOffers.length, hasMoreEntities, isAutoLoading]);
|
||||
|
||||
// Обновляем список товаров с предложениями при изменении накопленных товаров или цен
|
||||
useEffect(() => {
|
||||
if (!isPartsIndexMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Показываем все товары, но отдельно считаем те, у которых есть цены
|
||||
const entitiesWithOffers = accumulatedEntities;
|
||||
|
||||
// Подсчитываем количество товаров с реальными ценами для автоподгрузки
|
||||
const entitiesWithRealPrices = accumulatedEntities.filter(entity => {
|
||||
const productForPrice = { id: entity.id, code: entity.code, brand: entity.brand.name };
|
||||
const priceData = getPrice(productForPrice);
|
||||
return priceData && priceData.price && priceData.price > 0;
|
||||
});
|
||||
|
||||
console.log('📊 Обновляем entitiesWithOffers:', {
|
||||
накопленоТоваров: accumulatedEntities.length,
|
||||
отображаемыхТоваров: entitiesWithOffers.length,
|
||||
сРеальнымиЦенами: entitiesWithRealPrices.length,
|
||||
целевоеКоличество: ITEMS_PER_PAGE
|
||||
});
|
||||
|
||||
setEntitiesWithOffers(entitiesWithOffers);
|
||||
|
||||
// Показываем товары для текущей пользовательской страницы
|
||||
const startIndex = (currentUserPage - 1) * ITEMS_PER_PAGE;
|
||||
const endIndex = startIndex + ITEMS_PER_PAGE;
|
||||
const visibleForCurrentPage = entitiesWithOffers.slice(startIndex, endIndex);
|
||||
|
||||
console.log('📊 Обновляем visibleEntities:', {
|
||||
currentUserPage,
|
||||
startIndex,
|
||||
endIndex,
|
||||
visibleForCurrentPage: visibleForCurrentPage.length,
|
||||
entitiesWithOffers: entitiesWithOffers.length
|
||||
});
|
||||
|
||||
setVisibleEntities(visibleForCurrentPage);
|
||||
|
||||
}, [isPartsIndexMode, accumulatedEntities, currentUserPage]);
|
||||
|
||||
// Отдельный useEffect для обновления статистики цен (без влияния на visibleEntities)
|
||||
useEffect(() => {
|
||||
if (!isPartsIndexMode || accumulatedEntities.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Обновляем статистику каждые 2 секунды
|
||||
const timer = setTimeout(() => {
|
||||
const entitiesWithRealPrices = accumulatedEntities.filter(entity => {
|
||||
const productForPrice = { id: entity.id, code: entity.code, brand: entity.brand.name };
|
||||
const priceData = getPrice(productForPrice);
|
||||
return priceData && priceData.price && priceData.price > 0;
|
||||
});
|
||||
|
||||
console.log('💰 Обновление статистики цен:', {
|
||||
накопленоТоваров: accumulatedEntities.length,
|
||||
сРеальнымиЦенами: entitiesWithRealPrices.length,
|
||||
процентЗагрузки: Math.round((entitiesWithRealPrices.length / accumulatedEntities.length) * 100)
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [isPartsIndexMode, accumulatedEntities.length, getPrice]);
|
||||
|
||||
// Генерируем динамические фильтры для PartsAPI
|
||||
const generatePartsAPIFilters = useCallback((): FilterConfig[] => {
|
||||
if (!allArticles.length) return [];
|
||||
@ -431,9 +649,6 @@ export default function Catalog() {
|
||||
});
|
||||
}, [allArticles, searchQuery, selectedFilters]);
|
||||
|
||||
// Упрощенная логика - показываем все загруженные товары без клиентской фильтрации
|
||||
const filteredEntities = visibleEntities;
|
||||
|
||||
// Обновляем видимые артикулы при изменении поиска или фильтров для PartsAPI
|
||||
useEffect(() => {
|
||||
if (isPartsAPIMode) {
|
||||
@ -458,10 +673,14 @@ export default function Catalog() {
|
||||
if (searchQuery.trim() || Object.keys(selectedFilters).length > 0) {
|
||||
console.log('🔍 Поисковый запрос или фильтры изменились, сбрасываем пагинацию');
|
||||
setPartsIndexPage(1);
|
||||
setCurrentUserPage(1);
|
||||
setHasMoreEntities(true);
|
||||
setAccumulatedEntities([]);
|
||||
setEntitiesWithOffers([]);
|
||||
setEntitiesCache(new Map());
|
||||
|
||||
// Перезагружаем данные с новыми параметрами фильтрации
|
||||
const apiParams = convertFiltersToPartsIndexParams();
|
||||
const apiParams = convertFiltersToPartsIndexParams;
|
||||
const paramsString = Object.keys(apiParams).length > 0 ? JSON.stringify(apiParams) : undefined;
|
||||
|
||||
// Также обновляем параметры фильтрации
|
||||
@ -477,7 +696,7 @@ export default function Catalog() {
|
||||
catalogId: catalogId as string,
|
||||
groupId: groupId as string,
|
||||
lang: 'ru',
|
||||
limit: ITEMS_PER_PAGE,
|
||||
limit: PARTSINDEX_PAGE_SIZE,
|
||||
page: 1,
|
||||
q: searchQuery || undefined,
|
||||
params: paramsString
|
||||
@ -503,26 +722,55 @@ export default function Catalog() {
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
} else if (isPartsIndexMode && !entitiesLoading && !entitiesError) {
|
||||
// Для PartsIndex показываем пустое состояние если нет товаров
|
||||
setShowEmptyState(visibleEntities.length === 0);
|
||||
// Для PartsIndex показываем пустое состояние если нет товаров И данные уже загружены
|
||||
const hasLoadedData = accumulatedEntities.length > 0 || Boolean(entitiesData?.partsIndexCatalogEntities?.list);
|
||||
setShowEmptyState(hasLoadedData && visibleEntities.length === 0);
|
||||
console.log('📊 Определяем showEmptyState для PartsIndex:', {
|
||||
hasLoadedData,
|
||||
visibleEntitiesLength: visibleEntities.length,
|
||||
accumulatedEntitiesLength: accumulatedEntities.length,
|
||||
showEmptyState: hasLoadedData && visibleEntities.length === 0
|
||||
});
|
||||
} else {
|
||||
setShowEmptyState(false);
|
||||
}
|
||||
}, [isPartsAPIMode, articlesLoading, articlesError, visibleProductsCount, allArticles.length,
|
||||
isPartsIndexMode, entitiesLoading, entitiesError, visibleEntities.length, filteredEntities.length]);
|
||||
isPartsIndexMode, entitiesLoading, entitiesError, visibleEntities.length, accumulatedEntities.length, entitiesData]);
|
||||
|
||||
// Функции для навигации по страницам PartsIndex
|
||||
// Функции для навигации по пользовательским страницам
|
||||
const handleNextPage = useCallback(() => {
|
||||
if (hasMoreEntities && !entitiesLoading) {
|
||||
setPartsIndexPage(prev => prev + 1);
|
||||
const maxUserPage = Math.ceil(accumulatedEntities.length / ITEMS_PER_PAGE);
|
||||
console.log('🔄 Нажата кнопка "Вперед":', {
|
||||
currentUserPage,
|
||||
maxUserPage,
|
||||
accumulatedEntitiesLength: accumulatedEntities.length,
|
||||
ITEMS_PER_PAGE
|
||||
});
|
||||
if (currentUserPage < maxUserPage) {
|
||||
setCurrentUserPage(prev => {
|
||||
console.log('✅ Переходим на страницу:', prev + 1);
|
||||
return prev + 1;
|
||||
});
|
||||
} else {
|
||||
console.log('⚠️ Нельзя перейти вперед: уже на последней странице');
|
||||
}
|
||||
}, [hasMoreEntities, entitiesLoading]);
|
||||
}, [currentUserPage, accumulatedEntities.length]);
|
||||
|
||||
const handlePrevPage = useCallback(() => {
|
||||
if (partsIndexPage > 1 && !entitiesLoading) {
|
||||
setPartsIndexPage(prev => prev - 1);
|
||||
console.log('🔄 Нажата кнопка "Назад":', {
|
||||
currentUserPage,
|
||||
accumulatedEntitiesLength: accumulatedEntities.length
|
||||
});
|
||||
if (currentUserPage > 1) {
|
||||
setCurrentUserPage(prev => {
|
||||
const newPage = prev - 1;
|
||||
console.log('✅ Переходим на страницу:', newPage);
|
||||
return newPage;
|
||||
});
|
||||
} else {
|
||||
console.log('⚠️ Нельзя перейти назад: уже на первой странице');
|
||||
}
|
||||
}, [partsIndexPage, entitiesLoading]);
|
||||
}, [currentUserPage, accumulatedEntities.length]);
|
||||
|
||||
// Функция для загрузки следующей порции товаров по кнопке (только для PartsAPI)
|
||||
const handleLoadMorePartsAPI = useCallback(async () => {
|
||||
@ -592,9 +840,7 @@ export default function Catalog() {
|
||||
isPartsAPIMode ?
|
||||
(visibilityMap.size === 0 && allArticles.length > 0 ? undefined : visibleProductsCount) :
|
||||
isPartsIndexMode ?
|
||||
(searchQuery.trim() || Object.keys(selectedFilters).length > 0 ?
|
||||
filteredEntities.length :
|
||||
entitiesData?.partsIndexCatalogEntities?.pagination?.limit || visibleEntities.length) :
|
||||
entitiesWithOffers.length :
|
||||
3587
|
||||
}
|
||||
productName={
|
||||
@ -740,25 +986,21 @@ export default function Catalog() {
|
||||
)}
|
||||
|
||||
{/* Отображение товаров PartsIndex */}
|
||||
{isPartsIndexMode && filteredEntities.length > 0 && (
|
||||
{isPartsIndexMode && (() => {
|
||||
console.log('🎯 Проверяем отображение PartsIndex товаров:', {
|
||||
isPartsIndexMode,
|
||||
visibleEntitiesLength: visibleEntities.length,
|
||||
visibleEntities: visibleEntities.map(e => ({ id: e.id, code: e.code, brand: e.brand.name }))
|
||||
});
|
||||
return visibleEntities.length > 0;
|
||||
})() && (
|
||||
<>
|
||||
{filteredEntities
|
||||
{visibleEntities
|
||||
.map((entity, idx) => {
|
||||
const productForPrice = { id: entity.id, code: entity.code, brand: entity.brand.name };
|
||||
const priceData = getPrice(productForPrice);
|
||||
const isLoadingPriceData = isLoadingPrice(productForPrice);
|
||||
|
||||
return {
|
||||
entity,
|
||||
idx,
|
||||
productForPrice,
|
||||
priceData,
|
||||
isLoadingPriceData,
|
||||
hasOffer: priceData !== null || isLoadingPriceData
|
||||
};
|
||||
})
|
||||
.filter(item => item.hasOffer) // Показываем только товары с предложениями или загружающиеся
|
||||
.map(({ entity, idx, productForPrice, priceData, isLoadingPriceData }) => {
|
||||
// Определяем цену для отображения
|
||||
let displayPrice = "Цена по запросу";
|
||||
let displayCurrency = "RUB";
|
||||
@ -790,7 +1032,7 @@ export default function Catalog() {
|
||||
onAddToCart={async () => {
|
||||
// Если цена не загружена, загружаем её и добавляем в корзину
|
||||
if (!priceData && !isLoadingPriceData) {
|
||||
loadPriceOnDemand(productForPrice);
|
||||
ensurePriceLoaded(productForPrice);
|
||||
console.log('🔄 Загружаем цену для:', entity.code, entity.brand.name);
|
||||
return;
|
||||
}
|
||||
@ -843,40 +1085,61 @@ export default function Catalog() {
|
||||
{/* Пагинация для PartsIndex */}
|
||||
<div className="w-layout-hflex pagination">
|
||||
<button
|
||||
onClick={handlePrevPage}
|
||||
disabled={partsIndexPage <= 1 || entitiesLoading}
|
||||
onClick={() => {
|
||||
console.log('🖱️ Клик по кнопке "Назад"');
|
||||
handlePrevPage();
|
||||
}}
|
||||
disabled={currentUserPage <= 1}
|
||||
className="button_strock w-button mr-2"
|
||||
>
|
||||
← Назад
|
||||
</button>
|
||||
|
||||
<span className="flex items-center px-4 text-gray-600">
|
||||
Страница {partsIndexPage} {totalPages > partsIndexPage && `из ${totalPages}+`}
|
||||
Страница {currentUserPage} из {Math.ceil(accumulatedEntities.length / ITEMS_PER_PAGE) || 1}
|
||||
{isAutoLoading && ' (загружаем...)'}
|
||||
<span className="ml-2 text-xs text-gray-400">
|
||||
(товаров: {accumulatedEntities.length})
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<button
|
||||
onClick={handleNextPage}
|
||||
disabled={!hasMoreEntities || entitiesLoading}
|
||||
onClick={() => {
|
||||
console.log('🖱️ Клик по кнопке "Вперед"');
|
||||
handleNextPage();
|
||||
}}
|
||||
disabled={currentUserPage >= Math.ceil(accumulatedEntities.length / ITEMS_PER_PAGE)}
|
||||
className="button_strock w-button ml-2"
|
||||
>
|
||||
{entitiesLoading ? 'Загрузка...' : 'Вперед →'}
|
||||
Вперед →
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Отладочная информация */}
|
||||
{isPartsIndexMode && (
|
||||
<div className="text-xs text-gray-500 mt-4 p-2 bg-gray-100 rounded">
|
||||
<div>🔍 Отладка PartsIndex:</div>
|
||||
<div>• hasMoreItems: {hasMoreItems ? 'да' : 'нет'}</div>
|
||||
<div>• hasMoreEntities: {hasMoreEntities ? 'да' : 'нет'}</div>
|
||||
<div>• entitiesPage: {entitiesPage}</div>
|
||||
<div>🔍 Отладка PartsIndex (исправленная логика):</div>
|
||||
<div>• accumulatedEntities: {accumulatedEntities.length}</div>
|
||||
<div>• entitiesWithOffers: {entitiesWithOffers.length}</div>
|
||||
<div>• visibleEntities: {visibleEntities.length}</div>
|
||||
<div>• filteredEntities: {filteredEntities.length}</div>
|
||||
<div>• groupId: {groupId || 'отсутствует'}</div>
|
||||
<div>• isLoadingMore: {isLoadingMore ? 'да' : 'нет'}</div>
|
||||
<div>• currentUserPage: {currentUserPage}</div>
|
||||
<div>• partsIndexPage (API): {partsIndexPage}</div>
|
||||
<div>• isAutoLoading: {isAutoLoading ? 'да' : 'нет'}</div>
|
||||
<div>• hasMoreEntities: {hasMoreEntities ? 'да' : 'нет'}</div>
|
||||
<div>• entitiesLoading: {entitiesLoading ? 'да' : 'нет'}</div>
|
||||
<div>• catalogId: {catalogId || 'отсутствует'}</div>
|
||||
<div>• Пагинация: {JSON.stringify(entitiesData?.partsIndexCatalogEntities?.pagination)}</div>
|
||||
<div>• groupId: {groupId || 'отсутствует'}</div>
|
||||
<div>• Target: {ITEMS_PER_PAGE} товаров на страницу</div>
|
||||
<div>• showEmptyState: {showEmptyState ? 'да' : 'нет'}</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
console.log('🔧 Ручной запуск автоподгрузки');
|
||||
autoLoadMoreEntities();
|
||||
}}
|
||||
className="mt-2 px-3 py-1 bg-blue-500 text-white text-xs rounded"
|
||||
disabled={isAutoLoading}
|
||||
>
|
||||
{isAutoLoading ? 'Загружаем...' : 'Загрузить еще'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
@ -892,7 +1155,16 @@ export default function Catalog() {
|
||||
)}
|
||||
|
||||
{/* Пустое состояние для PartsIndex */}
|
||||
{isPartsIndexMode && !entitiesLoading && !entitiesError && showEmptyState && (
|
||||
{isPartsIndexMode && !entitiesLoading && !entitiesError && (() => {
|
||||
console.log('🎯 Проверяем пустое состояние PartsIndex:', {
|
||||
isPartsIndexMode,
|
||||
entitiesLoading,
|
||||
entitiesError,
|
||||
showEmptyState,
|
||||
visibleEntitiesLength: visibleEntities.length
|
||||
});
|
||||
return showEmptyState;
|
||||
})() && (
|
||||
<CatalogEmptyState
|
||||
categoryName={decodeURIComponent(categoryName as string || 'товаров')}
|
||||
hasFilters={searchQuery.trim() !== '' || Object.keys(selectedFilters).some(key => selectedFilters[key].length > 0)}
|
||||
|
Reference in New Issue
Block a user