catalog prices fix

This commit is contained in:
54CHA
2025-07-17 21:22:45 +03:00
parent 5fd2cf1b8c
commit b6f9d017d6
4 changed files with 56 additions and 59 deletions

View File

@ -1,6 +1,6 @@
import { PartsIndexCatalogsResponse, PartsIndexGroup, PartsIndexEntityInfoResponse } from '@/types/partsindex'; import { PartsIndexCatalogsResponse, PartsIndexGroup, PartsIndexEntityInfoResponse } from '@/types/partsindex';
const PARTS_INDEX_API_BASE = 'https://api.parts-index.com'; const PARTS_INDEX_API_BASE = process.env.PARTSAPI_URL+"/v1" || 'https://api.parts-index.com/v1';
const API_KEY = 'PI-E1C0ADB7-E4A8-4960-94A0-4D9C0A074DAE'; const API_KEY = 'PI-E1C0ADB7-E4A8-4960-94A0-4D9C0A074DAE';
class PartsIndexService { class PartsIndexService {

View File

@ -38,8 +38,8 @@ const mockData = Array(12).fill({
brand: "Borsehung", brand: "Borsehung",
}); });
const ITEMS_PER_PAGE = 50; // Целевое количество товаров на странице const ITEMS_PER_PAGE = 50; // Уменьшено для быстрой загрузки и лучшего UX
const PARTSINDEX_PAGE_SIZE = 25; // Размер страницы PartsIndex API (фиксированный) const PARTSINDEX_PAGE_SIZE = 25; // Синхронизировано для оптимальной скорости
const MAX_BRANDS_DISPLAY = 10; // Сколько брендов показывать изначально const MAX_BRANDS_DISPLAY = 10; // Сколько брендов показывать изначально
export default function Catalog() { export default function Catalog() {
@ -175,16 +175,17 @@ export default function Catalog() {
// Хук для загрузки цен товаров PartsIndex // Хук для загрузки цен товаров PartsIndex
const { getPrice, isLoadingPrice, ensurePriceLoaded } = useProductPrices(); const { getPrice, isLoadingPrice, ensurePriceLoaded } = useProductPrices();
// Загружаем цены для видимых товаров PartsIndex // Загружаем цены для видимых товаров PartsIndex (для отображения конкретных цен)
useEffect(() => { useEffect(() => {
if (isPartsIndexMode && visibleEntities.length > 0) { if (isPartsIndexMode && visibleEntities.length > 0) {
// Загружаем цены только для видимых товаров для отображения точных цен
visibleEntities.forEach((entity, index) => { visibleEntities.forEach((entity, index) => {
const productForPrice = { const productForPrice = {
id: entity.id, id: entity.id,
code: entity.code, code: entity.code,
brand: entity.brand.name brand: entity.brand.name
}; };
// Загружаем с небольшой задержкой чтобы не перегружать сервер // Загружаем с небольшой задержкой
setTimeout(() => { setTimeout(() => {
ensurePriceLoaded(productForPrice); ensurePriceLoaded(productForPrice);
}, index * 50); }, index * 50);
@ -284,25 +285,18 @@ export default function Catalog() {
// Восстанавливаем автоподгрузку // Восстанавливаем автоподгрузку
console.log('🔄 Автоподгрузка активна'); console.log('🔄 Автоподгрузка активна');
// Подсчитываем текущее количество товаров с предложениями // Подсчитываем текущее количество товаров (все уже отфильтрованы на сервере)
const currentEntitiesWithOffers = accumulatedEntities.filter(entity => { const currentEntitiesCount = accumulatedEntities.length;
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('📊 Автоподгрузка: текущее состояние:', { console.log('📊 Автоподгрузка: текущее состояние:', {
накопленоТоваров: accumulatedEntities.length, накопленоТоваров: currentEntitiesCount,
сПредложениями: currentEntitiesWithOffers.length,
целевоеКоличество: ITEMS_PER_PAGE, целевоеКоличество: ITEMS_PER_PAGE,
естьЕщеТовары: hasMoreEntities естьЕщеТовары: hasMoreEntities
}); });
// Если у нас уже достаточно товаров с предложениями, не загружаем // Если у нас уже достаточно товаров, не загружаем
if (currentEntitiesWithOffers.length >= ITEMS_PER_PAGE) { if (currentEntitiesCount >= ITEMS_PER_PAGE) {
console.log('✅ Автоподгрузка: достаточно товаров с предложениями'); console.log('✅ Автоподгрузка: достаточно товаров');
return; return;
} }
@ -444,26 +438,18 @@ export default function Catalog() {
} }
}, [isPartsIndexMode, entitiesWithOffers.length, hasMoreEntities, isAutoLoading]); }, [isPartsIndexMode, entitiesWithOffers.length, hasMoreEntities, isAutoLoading]);
// Обновляем список товаров с предложениями при изменении накопленных товаров или цен // Обновляем список товаров при изменении накопленных товаров (серверная фильтрация)
useEffect(() => { useEffect(() => {
if (!isPartsIndexMode) { if (!isPartsIndexMode) {
return; return;
} }
// Показываем все товары, но отдельно считаем те, у которых есть цены // Все товары уже отфильтрованы на сервере - показываем все накопленные
const entitiesWithOffers = accumulatedEntities; 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:', { console.log('📊 Обновляем entitiesWithOffers (серверная фильтрация):', {
накопленоТоваров: accumulatedEntities.length, накопленоТоваров: accumulatedEntities.length,
отображаемыхТоваров: entitiesWithOffers.length, отображаемыхТоваров: entitiesWithOffers.length,
сРеальнымиЦенами: entitiesWithRealPrices.length,
целевоеКоличество: ITEMS_PER_PAGE целевоеКоличество: ITEMS_PER_PAGE
}); });
@ -486,29 +472,7 @@ export default function Catalog() {
}, [isPartsIndexMode, accumulatedEntities, currentUserPage]); }, [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 // Генерируем динамические фильтры для PartsAPI
const generatePartsAPIFilters = useCallback((): FilterConfig[] => { const generatePartsAPIFilters = useCallback((): FilterConfig[] => {
@ -724,12 +688,18 @@ export default function Catalog() {
} else if (isPartsIndexMode && !entitiesLoading && !entitiesError) { } else if (isPartsIndexMode && !entitiesLoading && !entitiesError) {
// Для PartsIndex показываем пустое состояние если нет товаров И данные уже загружены // Для PartsIndex показываем пустое состояние если нет товаров И данные уже загружены
const hasLoadedData = accumulatedEntities.length > 0 || Boolean(entitiesData?.partsIndexCatalogEntities?.list); const hasLoadedData = accumulatedEntities.length > 0 || Boolean(entitiesData?.partsIndexCatalogEntities?.list);
setShowEmptyState(hasLoadedData && visibleEntities.length === 0);
console.log('📊 Определяем showEmptyState для PartsIndex:', { // Показываем пустое состояние если данные загружены и нет видимых товаров
// (товары уже отфильтрованы на сервере, поэтому не нужно ждать загрузки цен)
const shouldShowEmpty = hasLoadedData && visibleEntities.length === 0;
setShowEmptyState(shouldShowEmpty);
console.log('📊 Определяем showEmptyState для PartsIndex (серверная фильтрация):', {
hasLoadedData, hasLoadedData,
visibleEntitiesLength: visibleEntities.length, visibleEntitiesLength: visibleEntities.length,
accumulatedEntitiesLength: accumulatedEntities.length, accumulatedEntitiesLength: accumulatedEntities.length,
showEmptyState: hasLoadedData && visibleEntities.length === 0 shouldShowEmpty,
showEmptyState: shouldShowEmpty
}); });
} else { } else {
setShowEmptyState(false); setShowEmptyState(false);
@ -936,6 +906,8 @@ export default function Catalog() {
</div> </div>
)} )}
{/* Сообщение об ошибке */} {/* Сообщение об ошибке */}
{isPartsAPIMode && articlesError && ( {isPartsAPIMode && articlesError && (
<div className="flex justify-center items-center py-8"> <div className="flex justify-center items-center py-8">
@ -1001,16 +973,20 @@ export default function Catalog() {
const priceData = getPrice(productForPrice); const priceData = getPrice(productForPrice);
const isLoadingPriceData = isLoadingPrice(productForPrice); const isLoadingPriceData = isLoadingPrice(productForPrice);
// Определяем цену для отображения // Определяем цену для отображения (все товары уже отфильтрованы на сервере)
let displayPrice = "Цена по запросу"; let displayPrice = "";
let displayCurrency = "RUB"; let displayCurrency = "RUB";
let priceElement; let priceElement;
if (isLoadingPriceData) { if (isLoadingPriceData) {
// Показываем скелетон загрузки вместо текста
priceElement = <PriceSkeleton />; priceElement = <PriceSkeleton />;
} else if (priceData && priceData.price) { } else if (priceData && priceData.price) {
displayPrice = `${priceData.price.toLocaleString('ru-RU')}`; displayPrice = `${priceData.price.toLocaleString('ru-RU')}`;
displayCurrency = priceData.currency || "RUB"; displayCurrency = priceData.currency || "RUB";
} else {
// Если нет данных о цене, показываем скелетон (товар должен загрузиться)
priceElement = <PriceSkeleton />;
} }
return ( return (
@ -1021,7 +997,7 @@ export default function Catalog() {
articleNumber={entity.code} articleNumber={entity.code}
brandName={entity.brand.name} brandName={entity.brand.name}
image={entity.images?.[0] || ''} image={entity.images?.[0] || ''}
price={isLoadingPriceData ? "" : displayPrice} price={priceElement ? "" : displayPrice}
priceElement={priceElement} priceElement={priceElement}
oldPrice="" oldPrice=""
discount="" discount=""

View File

@ -9,7 +9,7 @@ async function testPartsIndexAPI() {
// Получаем каталоги // Получаем каталоги
console.log('\n📦 Получаем список каталогов...'); console.log('\n📦 Получаем список каталогов...');
const catalogsResponse = await fetch('https://api.parts-index.com/v1/catalogs?lang=ru', { const catalogsResponse = await fetch(process.env.PARTSAPI_URL+"/v1/catalogs?lang=ru", {
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
}, },
@ -31,7 +31,7 @@ async function testPartsIndexAPI() {
console.log(`\n🎯 Получаем группы для каталога "${firstCatalog.name}"...`); console.log(`\n🎯 Получаем группы для каталога "${firstCatalog.name}"...`);
const groupsResponse = await fetch( const groupsResponse = await fetch(
`https://api.parts-index.com/v1/catalogs/${firstCatalog.id}/groups?lang=ru`, `${process.env.PARTSAPI_URL}/v1/catalogs/${firstCatalog.id}/groups?lang=ru`,
{ {
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',

21
user_input.py Normal file
View File

@ -0,0 +1,21 @@
def main():
while True:
print("\n" + "="*50)
user_input = input("Please provide feedback or next task (type 'stop' to exit): ").strip()
if user_input.lower() == 'stop':
print("Exiting task loop. Thank you!")
break
elif user_input.lower() == '':
print("Please provide some input or type 'stop' to exit.")
continue
else:
print(f"\nReceived input: {user_input}")
print("Processing your request...")
# Here the main process would handle the user's input
return user_input
if __name__ == "__main__":
result = main()
if result and result.lower() != 'stop':
print(f"Next task received: {result}")