Завершение rebase с обновленными компонентами
This commit is contained in:
@ -54,26 +54,40 @@ const createFilters = (result: any, loadedAnalogs: any): FilterConfig[] => {
|
||||
});
|
||||
}
|
||||
|
||||
// Фильтр по цене
|
||||
const prices: number[] = [];
|
||||
// Получаем все доступные предложения для расчета диапазонов
|
||||
const allAvailableOffers: any[] = [];
|
||||
|
||||
// Добавляем основные предложения
|
||||
result.internalOffers?.forEach((offer: any) => {
|
||||
if (offer.price > 0) prices.push(offer.price);
|
||||
allAvailableOffers.push(offer);
|
||||
});
|
||||
result.externalOffers?.forEach((offer: any) => {
|
||||
if (offer.price > 0) prices.push(offer.price);
|
||||
allAvailableOffers.push(offer);
|
||||
});
|
||||
|
||||
// Добавляем цены аналогов
|
||||
// Добавляем предложения аналогов
|
||||
Object.values(loadedAnalogs).forEach((analog: any) => {
|
||||
analog.internalOffers?.forEach((offer: any) => {
|
||||
if (offer.price > 0) prices.push(offer.price);
|
||||
allAvailableOffers.push({
|
||||
...offer,
|
||||
deliveryDuration: offer.deliveryDays
|
||||
});
|
||||
});
|
||||
analog.externalOffers?.forEach((offer: any) => {
|
||||
if (offer.price > 0) prices.push(offer.price);
|
||||
allAvailableOffers.push({
|
||||
...offer,
|
||||
deliveryDuration: offer.deliveryTime
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (prices.length > 0) {
|
||||
// Фильтр по цене - только если есть предложения с разными ценами
|
||||
const prices: number[] = [];
|
||||
allAvailableOffers.forEach((offer: any) => {
|
||||
if (offer.price > 0) prices.push(offer.price);
|
||||
});
|
||||
|
||||
if (prices.length > 1) {
|
||||
const minPrice = Math.min(...prices);
|
||||
const maxPrice = Math.max(...prices);
|
||||
|
||||
@ -87,26 +101,14 @@ const createFilters = (result: any, loadedAnalogs: any): FilterConfig[] => {
|
||||
}
|
||||
}
|
||||
|
||||
// Фильтр по сроку доставки
|
||||
// Фильтр по сроку доставки - только если есть предложения с разными сроками
|
||||
const deliveryDays: number[] = [];
|
||||
result.internalOffers?.forEach((offer: any) => {
|
||||
if (offer.deliveryDays && offer.deliveryDays > 0) deliveryDays.push(offer.deliveryDays);
|
||||
});
|
||||
result.externalOffers?.forEach((offer: any) => {
|
||||
if (offer.deliveryTime && offer.deliveryTime > 0) deliveryDays.push(offer.deliveryTime);
|
||||
});
|
||||
|
||||
// Добавляем сроки доставки аналогов
|
||||
Object.values(loadedAnalogs).forEach((analog: any) => {
|
||||
analog.internalOffers?.forEach((offer: any) => {
|
||||
if (offer.deliveryDays && offer.deliveryDays > 0) deliveryDays.push(offer.deliveryDays);
|
||||
});
|
||||
analog.externalOffers?.forEach((offer: any) => {
|
||||
if (offer.deliveryTime && offer.deliveryTime > 0) deliveryDays.push(offer.deliveryTime);
|
||||
});
|
||||
allAvailableOffers.forEach((offer: any) => {
|
||||
const days = offer.deliveryDays || offer.deliveryTime || offer.deliveryDuration;
|
||||
if (days && days > 0) deliveryDays.push(days);
|
||||
});
|
||||
|
||||
if (deliveryDays.length > 0) {
|
||||
if (deliveryDays.length > 1) {
|
||||
const minDays = Math.min(...deliveryDays);
|
||||
const maxDays = Math.max(...deliveryDays);
|
||||
|
||||
@ -120,26 +122,13 @@ const createFilters = (result: any, loadedAnalogs: any): FilterConfig[] => {
|
||||
}
|
||||
}
|
||||
|
||||
// Фильтр по количеству наличия
|
||||
// Фильтр по количеству наличия - только если есть предложения с разными количествами
|
||||
const quantities: number[] = [];
|
||||
result.internalOffers?.forEach((offer: any) => {
|
||||
allAvailableOffers.forEach((offer: any) => {
|
||||
if (offer.quantity && offer.quantity > 0) quantities.push(offer.quantity);
|
||||
});
|
||||
result.externalOffers?.forEach((offer: any) => {
|
||||
if (offer.quantity && offer.quantity > 0) quantities.push(offer.quantity);
|
||||
});
|
||||
|
||||
// Добавляем количества аналогов
|
||||
Object.values(loadedAnalogs).forEach((analog: any) => {
|
||||
analog.internalOffers?.forEach((offer: any) => {
|
||||
if (offer.quantity && offer.quantity > 0) quantities.push(offer.quantity);
|
||||
});
|
||||
analog.externalOffers?.forEach((offer: any) => {
|
||||
if (offer.quantity && offer.quantity > 0) quantities.push(offer.quantity);
|
||||
});
|
||||
});
|
||||
|
||||
if (quantities.length > 0) {
|
||||
if (quantities.length > 1) {
|
||||
const minQuantity = Math.min(...quantities);
|
||||
const maxQuantity = Math.max(...quantities);
|
||||
|
||||
@ -163,35 +152,24 @@ const getBestOffers = (offers: any[]) => {
|
||||
if (validOffers.length === 0) return [];
|
||||
|
||||
const result: { offer: any; type: string }[] = [];
|
||||
const usedOfferIds = new Set<string>();
|
||||
|
||||
// 1. Самая низкая цена (среди всех предложений)
|
||||
const lowestPriceOffer = [...validOffers].sort((a, b) => a.price - b.price)[0];
|
||||
if (lowestPriceOffer) {
|
||||
result.push({ offer: lowestPriceOffer, type: 'Самая низкая цена' });
|
||||
usedOfferIds.add(`${lowestPriceOffer.articleNumber}-${lowestPriceOffer.price}-${lowestPriceOffer.deliveryDuration}`);
|
||||
}
|
||||
|
||||
// 2. Самый дешевый аналог (только среди аналогов)
|
||||
// 2. Самый дешевый аналог (только среди аналогов) - всегда показываем если есть аналоги
|
||||
const analogOffers = validOffers.filter(offer => offer.isAnalog);
|
||||
if (analogOffers.length > 0) {
|
||||
const cheapestAnalogOffer = [...analogOffers].sort((a, b) => a.price - b.price)[0];
|
||||
const analogId = `${cheapestAnalogOffer.articleNumber}-${cheapestAnalogOffer.price}-${cheapestAnalogOffer.deliveryDuration}`;
|
||||
|
||||
if (!usedOfferIds.has(analogId)) {
|
||||
result.push({ offer: cheapestAnalogOffer, type: 'Самый дешевый аналог' });
|
||||
usedOfferIds.add(analogId);
|
||||
}
|
||||
result.push({ offer: cheapestAnalogOffer, type: 'Самый дешевый аналог' });
|
||||
}
|
||||
|
||||
// 3. Самая быстрая доставка (среди всех предложений)
|
||||
const fastestDeliveryOffer = [...validOffers].sort((a, b) => a.deliveryDuration - b.deliveryDuration)[0];
|
||||
if (fastestDeliveryOffer) {
|
||||
const fastestId = `${fastestDeliveryOffer.articleNumber}-${fastestDeliveryOffer.price}-${fastestDeliveryOffer.deliveryDuration}`;
|
||||
|
||||
if (!usedOfferIds.has(fastestId)) {
|
||||
result.push({ offer: fastestDeliveryOffer, type: 'Самая быстрая доставка' });
|
||||
}
|
||||
result.push({ offer: fastestDeliveryOffer, type: 'Самая быстрая доставка' });
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -376,7 +354,101 @@ export default function SearchResult() {
|
||||
|
||||
const hasOffers = result && (result.internalOffers.length > 0 || result.externalOffers.length > 0);
|
||||
const hasAnalogs = result && result.analogs.length > 0;
|
||||
const searchResultFilters = createFilters(result, loadedAnalogs);
|
||||
|
||||
// Создаем динамические фильтры на основе доступных данных с учетом активных фильтров
|
||||
const searchResultFilters = useMemo(() => {
|
||||
const baseFilters = createFilters(result, loadedAnalogs);
|
||||
|
||||
// Если нет активных фильтров, возвращаем базовые фильтры
|
||||
if (!filtersAreActive) {
|
||||
return baseFilters;
|
||||
}
|
||||
|
||||
// Создаем динамические фильтры с учетом других активных фильтров
|
||||
return baseFilters.map(filter => {
|
||||
if (filter.type !== 'range') {
|
||||
return filter;
|
||||
}
|
||||
|
||||
// Для каждого диапазонного фильтра пересчитываем границы на основе
|
||||
// предложений, отфильтрованных другими фильтрами (исключая текущий)
|
||||
let relevantOffers = allOffers;
|
||||
|
||||
// Применяем все фильтры кроме текущего
|
||||
relevantOffers = allOffers.filter(offer => {
|
||||
// Фильтр по бренду (если это не фильтр производителя)
|
||||
if (filter.title !== 'Производитель' && selectedBrands.length > 0 && !selectedBrands.includes(offer.brand)) {
|
||||
return false;
|
||||
}
|
||||
// Фильтр по цене (если это не фильтр цены)
|
||||
if (filter.title !== 'Цена (₽)' && priceRange && (offer.price < priceRange[0] || offer.price > priceRange[1])) {
|
||||
return false;
|
||||
}
|
||||
// Фильтр по сроку доставки (если это не фильтр доставки)
|
||||
if (filter.title !== 'Срок доставки (дни)' && deliveryRange) {
|
||||
const deliveryDays = offer.deliveryDuration;
|
||||
if (deliveryDays < deliveryRange[0] || deliveryDays > deliveryRange[1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Фильтр по количеству (если это не фильтр количества)
|
||||
if (filter.title !== 'Количество (шт.)' && quantityRange) {
|
||||
const quantity = offer.quantity;
|
||||
if (quantity < quantityRange[0] || quantity > quantityRange[1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Фильтр по поисковой строке
|
||||
if (filterSearchTerm) {
|
||||
const searchTerm = filterSearchTerm.toLowerCase();
|
||||
const brandMatch = offer.brand.toLowerCase().includes(searchTerm);
|
||||
const articleMatch = offer.articleNumber.toLowerCase().includes(searchTerm);
|
||||
const nameMatch = offer.name.toLowerCase().includes(searchTerm);
|
||||
if (!brandMatch && !articleMatch && !nameMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Пересчитываем диапазон на основе отфильтрованных предложений
|
||||
if (filter.title === 'Цена (₽)') {
|
||||
const prices = relevantOffers.filter(o => o.price > 0).map(o => o.price);
|
||||
if (prices.length > 0) {
|
||||
return {
|
||||
...filter,
|
||||
min: Math.floor(Math.min(...prices)),
|
||||
max: Math.ceil(Math.max(...prices))
|
||||
};
|
||||
}
|
||||
} else if (filter.title === 'Срок доставки (дни)') {
|
||||
const deliveryDays = relevantOffers
|
||||
.map(o => o.deliveryDuration)
|
||||
.filter(d => d && d > 0);
|
||||
if (deliveryDays.length > 0) {
|
||||
return {
|
||||
...filter,
|
||||
min: Math.min(...deliveryDays),
|
||||
max: Math.max(...deliveryDays)
|
||||
};
|
||||
}
|
||||
} else if (filter.title === 'Количество (шт.)') {
|
||||
const quantities = relevantOffers
|
||||
.map(o => o.quantity)
|
||||
.filter(q => q && q > 0);
|
||||
if (quantities.length > 0) {
|
||||
return {
|
||||
...filter,
|
||||
min: Math.min(...quantities),
|
||||
max: Math.max(...quantities)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return filter;
|
||||
});
|
||||
}, [result, loadedAnalogs, filtersAreActive, allOffers, selectedBrands, priceRange, deliveryRange, quantityRange, filterSearchTerm]);
|
||||
|
||||
const bestOffersData = getBestOffers(filteredOffers);
|
||||
|
||||
|
||||
@ -406,6 +478,8 @@ export default function SearchResult() {
|
||||
}
|
||||
}, [q, article, router.query]);
|
||||
|
||||
|
||||
|
||||
// Удаляем старую заглушку - теперь обрабатываем все типы поиска
|
||||
|
||||
const minPrice = useMemo(() => {
|
||||
|
@ -74,6 +74,8 @@ const VehicleDetailsPage = () => {
|
||||
});
|
||||
const [selectedNode, setSelectedNode] = useState<any | null>(null);
|
||||
const [selectedQuickGroup, setSelectedQuickGroup] = useState<any | null>(null);
|
||||
const [selectedParts, setSelectedParts] = useState<Set<string | number>>(new Set());
|
||||
const [highlightedPart, setHighlightedPart] = useState<string | number | null>(null);
|
||||
|
||||
// Получаем информацию о выбранном автомобиле
|
||||
const ssdFromQuery = Array.isArray(router.query.ssd) ? router.query.ssd[0] : router.query.ssd;
|
||||
@ -138,6 +140,20 @@ const VehicleDetailsPage = () => {
|
||||
);
|
||||
|
||||
// Получаем детали выбранного узла, если он выбран
|
||||
console.log('🔍 [vehicleId].tsx - Проверка условий для GET_LAXIMO_UNIT_DETAILS:', {
|
||||
selectedNode: selectedNode ? {
|
||||
unitid: selectedNode.unitid,
|
||||
name: selectedNode.name,
|
||||
hasSsd: !!selectedNode.ssd
|
||||
} : null,
|
||||
skipCondition: !selectedNode,
|
||||
catalogCode: selectedNode?.catalogCode || selectedNode?.catalog || brand,
|
||||
vehicleId: selectedNode?.vehicleId || vehicleId,
|
||||
unitId: selectedNode?.unitid || selectedNode?.unitId,
|
||||
ssd: selectedNode?.ssd || finalSsd || '',
|
||||
finalSsd: finalSsd ? `${finalSsd.substring(0, 50)}...` : 'отсутствует'
|
||||
});
|
||||
|
||||
const {
|
||||
data: unitDetailsData,
|
||||
loading: unitDetailsLoading,
|
||||
@ -155,6 +171,23 @@ const VehicleDetailsPage = () => {
|
||||
: { catalogCode: '', vehicleId: '', unitId: '', ssd: '' },
|
||||
skip: !selectedNode,
|
||||
errorPolicy: 'all',
|
||||
fetchPolicy: 'no-cache',
|
||||
notifyOnNetworkStatusChange: true,
|
||||
onCompleted: (data) => {
|
||||
console.log('🔍 [vehicleId].tsx - GET_LAXIMO_UNIT_DETAILS completed:', {
|
||||
detailsCount: data?.laximoUnitDetails?.length || 0,
|
||||
firstDetail: data?.laximoUnitDetails?.[0],
|
||||
allDetails: data?.laximoUnitDetails?.map((detail: any) => ({
|
||||
name: detail.name,
|
||||
oem: detail.oem,
|
||||
codeonimage: detail.codeonimage,
|
||||
attributesCount: detail.attributes?.length || 0
|
||||
}))
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error('❌ [vehicleId].tsx - GET_LAXIMO_UNIT_DETAILS error:', error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@ -234,6 +267,22 @@ const VehicleDetailsPage = () => {
|
||||
|
||||
const unitDetails = unitDetailsData?.laximoUnitDetails || [];
|
||||
|
||||
// Детальное логирование данных от API
|
||||
React.useEffect(() => {
|
||||
if (unitDetailsData?.laximoUnitDetails) {
|
||||
console.log('🔍 [vehicleId].tsx - Полные данные unitDetails от API:', {
|
||||
totalParts: unitDetailsData.laximoUnitDetails.length,
|
||||
firstPart: unitDetailsData.laximoUnitDetails[0],
|
||||
allCodeOnImages: unitDetailsData.laximoUnitDetails.map((part: any) => ({
|
||||
name: part.name,
|
||||
codeonimage: part.codeonimage,
|
||||
detailid: part.detailid,
|
||||
oem: part.oem
|
||||
}))
|
||||
});
|
||||
}
|
||||
}, [unitDetailsData]);
|
||||
|
||||
// Логируем ошибки
|
||||
if (vehicleError) {
|
||||
console.error('Vehicle GraphQL error:', vehicleError);
|
||||
@ -382,6 +431,9 @@ const VehicleDetailsPage = () => {
|
||||
});
|
||||
|
||||
setSelectedNode(node);
|
||||
// Сброс состояния выбранных деталей при открытии нового узла
|
||||
setSelectedParts(new Set());
|
||||
setHighlightedPart(null);
|
||||
router.push(
|
||||
{ pathname: router.pathname, query: { ...router.query, unitid: node.unitid || node.id } },
|
||||
undefined,
|
||||
@ -391,6 +443,9 @@ const VehicleDetailsPage = () => {
|
||||
// Закрыть KnotIn и удалить unitid из URL
|
||||
const closeKnot = () => {
|
||||
setSelectedNode(null);
|
||||
// Сброс состояния выбранных деталей при закрытии узла
|
||||
setSelectedParts(new Set());
|
||||
setHighlightedPart(null);
|
||||
const { unitid, ...rest } = router.query;
|
||||
router.push(
|
||||
{ pathname: router.pathname, query: rest },
|
||||
@ -399,6 +454,25 @@ const VehicleDetailsPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// Обработчик выбора детали (множественный выбор)
|
||||
const handlePartSelect = (codeOnImage: string | number | null) => {
|
||||
if (codeOnImage === null) return; // Игнорируем null значения
|
||||
setSelectedParts(prev => {
|
||||
const newSet = new Set(prev);
|
||||
if (newSet.has(codeOnImage)) {
|
||||
newSet.delete(codeOnImage); // Убираем если уже выбрана
|
||||
} else {
|
||||
newSet.add(codeOnImage); // Добавляем если не выбрана
|
||||
}
|
||||
return newSet;
|
||||
});
|
||||
};
|
||||
|
||||
// Обработчик подсветки детали при наведении
|
||||
const handlePartHighlight = (codeOnImage: string | number | null) => {
|
||||
setHighlightedPart(codeOnImage);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<MetaTags {...metaData} />
|
||||
@ -551,6 +625,9 @@ const VehicleDetailsPage = () => {
|
||||
unitId={selectedNode.unitid}
|
||||
unitName={selectedNode.name}
|
||||
parts={unitDetails}
|
||||
onPartSelect={handlePartSelect}
|
||||
onPartsHighlight={handlePartHighlight}
|
||||
selectedParts={selectedParts}
|
||||
/>
|
||||
{unitDetailsLoading ? (
|
||||
<div style={{ padding: 24, textAlign: 'center' }}>Загружаем детали узла...</div>
|
||||
@ -561,6 +638,10 @@ const VehicleDetailsPage = () => {
|
||||
parts={unitDetails}
|
||||
catalogCode={vehicleInfo.catalog}
|
||||
vehicleId={vehicleInfo.vehicleid}
|
||||
highlightedCodeOnImage={highlightedPart}
|
||||
selectedParts={selectedParts}
|
||||
onPartSelect={handlePartSelect}
|
||||
onPartHover={handlePartHighlight}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ padding: 24, textAlign: 'center' }}>Детали не найдены</div>
|
||||
|
Reference in New Issue
Block a user