@ -38,7 +38,8 @@ const mockData = Array(12).fill({
brand : "Borsehung" ,
} ) ;
const ITEMS_PER_PAGE = 2 0;
const ITEMS_PER_PAGE = 5 0; // Целевое количество товаров на странице
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_PAG E,
limit : PARTSINDEX_PAGE_SIZ E,
page : partsIndexPage ,
q : searchQuery || undefined ,
params : undefined // Будем обновлять через refetch
@ -164,12 +173,24 @@ export default function Catalog() {
// allEntities больше не используется - используем allLoadedEntities
// Хук для загрузки цен товаров PartsIndex
const productsForPrices = v isibleEntities . 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 ) {
v isibleEntities . 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_PAG E,
limit : PARTSINDEX_PAGE_SIZ E,
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 ( visible Entities. length === 0 ) ;
// Для PartsIndex показываем пустое состояние если нет товаров И данные уже загружены
const hasLoadedData = accumulated Entities. 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 , filter edEntities. length ] ) ;
isPartsIndexMode , entitiesLoading , entitiesError , visibleEntities . length , accumulat edEntities. length , entitiesData ]) ;
// Функции для навигации по страницам PartsIndex
// Функции для навигации по пользовательским страницам
const handleNextPage = useCallback ( ( ) = > {
if ( hasMoreEntities && ! entitiesLoading ) {
setPartsIndexPag e ( prev = > prev + 1 ) ;
const maxUserPage = Math . ceil ( accumulatedEntities . length / ITEMS_PER_PAGE ) ;
consol e. log ( '🔄 Нажата кнопка "Вперед":' , {
currentUserPage ,
maxUserPage ,
accumulatedEntitiesLength : accumulatedEntities.length ,
ITEMS_PER_PAGE
} ) ;
if ( currentUserPage < maxUserPage ) {
setCurrentUserPage ( prev = > {
console . log ( '✅ Переходим на страницу:' , prev + 1 ) ;
return prev + 1 ;
} ) ;
} else {
console . log ( '⚠️ Нельзя перейти вперед: уже на последней странице' ) ;
}
} , [ hasMo reE ntities , entitiesLoading ] ) ;
} , [ cur rentUserPage , 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 ;
} ) ( ) && (
< >
{ filtered Entities
{ visible Entities
. 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 ) {
loadPriceOnDeman d( productForPrice ) ;
ensurePriceLoade d( 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 > • isAuto Loading : { isAuto Loading ? 'да' : 'нет' } < / div >
< div > • hasMoreEntities : { hasMoreEntities ? 'да' : 'нет' } < / div >
< div > • entitiesLoading : { entitiesLoading ? 'да' : 'нет' } < / div >
< div > • catalog Id: { catalog Id || 'отсутствует' } < / div >
< div > • П а г и н а ц и я : { JSON . stringify ( entitiesData ? . partsIndexCatalogEntities ? . pagination ) } < / div >
< div > • group Id: { group Id || 'отсутствует' } < / 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 ) }