613 lines
28 KiB
TypeScript
613 lines
28 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import { useRouter } from 'next/router';
|
||
import { useQuery } from '@apollo/client';
|
||
import Head from 'next/head';
|
||
import Footer from '@/components/Footer';
|
||
import Layout from '@/components/Layout';
|
||
import VehiclePartsSearchSection from '@/components/VehiclePartsSearchSection';
|
||
import LaximoDiagnostic from '@/components/LaximoDiagnostic';
|
||
import { GET_LAXIMO_VEHICLE_INFO, GET_LAXIMO_CATALOG_INFO, GET_LAXIMO_UNIT_DETAILS } from '@/lib/graphql';
|
||
import { LaximoCatalogInfo } from '@/types/laximo';
|
||
import InfoVin from '@/components/vin/InfoVin';
|
||
import VinLeftbar from '@/components/vin/VinLeftbar';
|
||
import VinKnot from '@/components/vin/VinKnot';
|
||
import VinCategory from '@/components/vin/VinCategory';
|
||
import PartDetailCard from '@/components/PartDetailCard';
|
||
import VinPartCard from '@/components/vin/VinPartCard';
|
||
import KnotIn from '@/components/vin/KnotIn';
|
||
import KnotParts from '@/components/vin/KnotParts';
|
||
import VinQuick from '@/components/vin/VinQuick';
|
||
import CatalogSubscribe from '@/components/CatalogSubscribe';
|
||
import MobileMenuBottomSection from '@/components/MobileMenuBottomSection';
|
||
import MetaTags from '@/components/MetaTags';
|
||
import { getMetaByPath } from '@/lib/meta-config';
|
||
|
||
|
||
interface LaximoVehicleInfo {
|
||
vehicleid: string;
|
||
name: string;
|
||
ssd: string;
|
||
brand: string;
|
||
catalog: string;
|
||
attributes: Array<{
|
||
key: string;
|
||
name: string;
|
||
value: string;
|
||
}>;
|
||
}
|
||
|
||
const VehicleDetailsPage = () => {
|
||
const router = useRouter();
|
||
const { brand, vehicleId, oemNumber, searchType: searchTypeParam } = router.query;
|
||
|
||
// Устанавливаем тип поиска из URL или по умолчанию
|
||
// Важно: согласно документации Laximo, для групп быстрого поиска используется ListQuickGroup
|
||
// Если в URL передан searchType=categories, мы интерпретируем это как запрос на quickgroups
|
||
let defaultSearchType: 'quickgroups' | 'categories' | 'fulltext' = 'quickgroups';
|
||
|
||
if (searchTypeParam === 'categories') {
|
||
// В URL categories, но мы используем quickgroups для групп быстрого поиска
|
||
defaultSearchType = 'quickgroups';
|
||
|
||
} else if (searchTypeParam === 'quickgroups') {
|
||
defaultSearchType = 'quickgroups';
|
||
} else if (searchTypeParam === 'fulltext') {
|
||
defaultSearchType = 'fulltext';
|
||
}
|
||
|
||
// ====== ВСЕ ХУКИ В НАЧАЛЕ КОМПОНЕНТА ======
|
||
const [searchType, setSearchType] = useState<'quickgroups' | 'categories' | 'fulltext'>(defaultSearchType);
|
||
const [showKnot, setShowKnot] = useState(false);
|
||
const [foundParts, setFoundParts] = useState<any[]>([]);
|
||
const [activeTab, setActiveTab] = useState<'uzly' | 'manufacturer'>('uzly');
|
||
const [openedPath, setOpenedPath] = useState<string[]>([]);
|
||
const [searchState, setSearchState] = useState<{
|
||
loading: boolean;
|
||
error: any;
|
||
query: string;
|
||
isSearching: boolean;
|
||
}>({
|
||
loading: false,
|
||
error: null,
|
||
query: '',
|
||
isSearching: false
|
||
});
|
||
const [selectedNode, setSelectedNode] = useState<any | null>(null);
|
||
const [selectedQuickGroup, setSelectedQuickGroup] = useState<any | null>(null);
|
||
const handleCategoryClick = (e?: React.MouseEvent) => {
|
||
if (e) e.preventDefault();
|
||
setShowKnot(true);
|
||
};
|
||
useEffect(() => {
|
||
const handler = (e: Event) => {
|
||
const target = e.target as HTMLElement;
|
||
if (target.classList.contains('link-2')) {
|
||
e.preventDefault();
|
||
setShowKnot(true);
|
||
}
|
||
};
|
||
document.addEventListener('click', handler);
|
||
return () => document.removeEventListener('click', handler);
|
||
}, []);
|
||
// ====== КОНЕЦ ХУКОВ ======
|
||
|
||
// Получаем информацию о каталоге
|
||
const { data: catalogData } = useQuery<{ laximoCatalogInfo: LaximoCatalogInfo }>(
|
||
GET_LAXIMO_CATALOG_INFO,
|
||
{
|
||
variables: { catalogCode: brand },
|
||
skip: !brand
|
||
}
|
||
);
|
||
|
||
// Получаем информацию о выбранном автомобиле
|
||
const ssdFromQuery = Array.isArray(router.query.ssd) ? router.query.ssd[0] : router.query.ssd;
|
||
const useStorage = router.query.use_storage === '1';
|
||
const ssdLengthFromUrl = router.query.ssd_length ? parseInt(router.query.ssd_length as string) : 0;
|
||
|
||
// Если указано use_storage, пытаемся получить SSD из localStorage
|
||
let finalSsd = '';
|
||
if (useStorage && typeof window !== 'undefined') {
|
||
const vehicleKey = `vehicle_ssd_${brand}_${vehicleId}`;
|
||
const storedSsd = localStorage.getItem(vehicleKey);
|
||
if (storedSsd) {
|
||
finalSsd = storedSsd;
|
||
// НЕ ОЧИЩАЕМ SSD сразу, оставляем на случай перезагрузки страницы
|
||
// localStorage.removeItem(vehicleKey);
|
||
}
|
||
} else if (ssdFromQuery && ssdFromQuery.trim() !== '') {
|
||
finalSsd = ssdFromQuery;
|
||
}
|
||
|
||
|
||
|
||
const { data: vehicleData, loading: vehicleLoading, error: vehicleError } = useQuery<{ laximoVehicleInfo: LaximoVehicleInfo }>(
|
||
GET_LAXIMO_VEHICLE_INFO,
|
||
{
|
||
variables: {
|
||
catalogCode: brand,
|
||
vehicleId: vehicleId,
|
||
...(finalSsd && { ssd: finalSsd }),
|
||
localized: true
|
||
},
|
||
skip: !brand || vehicleId === undefined || vehicleId === null,
|
||
errorPolicy: 'all',
|
||
onCompleted: (data) => {
|
||
console.log('🔍 VehicleInfo GraphQL completed:', {
|
||
requestedVehicleId: vehicleId,
|
||
returnedVehicleId: data?.laximoVehicleInfo?.vehicleid,
|
||
vehicleName: data?.laximoVehicleInfo?.name,
|
||
ssdUsed: finalSsd?.substring(0, 50) + '...',
|
||
fullData: data
|
||
});
|
||
|
||
if (data?.laximoVehicleInfo?.vehicleid !== vehicleId) {
|
||
console.log('🚨 ОБНАРУЖЕНО НЕСООТВЕТСТВИЕ VEHICLE ID!');
|
||
console.log(`📍 URL vehicleId: ${vehicleId}`);
|
||
console.log(`📍 API vehicleId: ${data?.laximoVehicleInfo?.vehicleid}`);
|
||
} else {
|
||
console.log('✅ Vehicle ID соответствует URL');
|
||
}
|
||
},
|
||
onError: (error) => {
|
||
console.error('❌ VehicleInfo GraphQL error:', error);
|
||
}
|
||
}
|
||
);
|
||
|
||
// Автоматическое перенаправление на правильный vehicleId если API вернул другой ID
|
||
useEffect(() => {
|
||
if (vehicleData?.laximoVehicleInfo && vehicleData.laximoVehicleInfo.vehicleid !== vehicleId) {
|
||
const correctVehicleId = vehicleData.laximoVehicleInfo.vehicleid;
|
||
console.log(`🔄 Автоматическое перенаправление: ${vehicleId} -> ${correctVehicleId}`);
|
||
|
||
// Обновляем localStorage с правильным ключом
|
||
if (finalSsd && typeof window !== 'undefined') {
|
||
const oldKey = `vehicle_ssd_${brand}_${vehicleId}`;
|
||
const newKey = `vehicle_ssd_${brand}_${correctVehicleId}`;
|
||
|
||
// Перемещаем SSD на правильный ключ
|
||
localStorage.setItem(newKey, finalSsd);
|
||
localStorage.removeItem(oldKey);
|
||
console.log(`💾 SSD перемещен: ${oldKey} -> ${newKey}`);
|
||
}
|
||
|
||
// Строим новый URL с правильным vehicleId
|
||
const currentParams = new URLSearchParams(window.location.search);
|
||
const newUrl = `/vehicle-search/${brand}/${correctVehicleId}?${currentParams.toString()}`;
|
||
|
||
// Перенаправляем на правильный URL
|
||
router.replace(newUrl);
|
||
return;
|
||
}
|
||
}, [vehicleData, vehicleId, brand, finalSsd, router]);
|
||
|
||
// Получаем детали выбранного узла, если он выбран
|
||
const {
|
||
data: unitDetailsData,
|
||
loading: unitDetailsLoading,
|
||
error: unitDetailsError
|
||
} = useQuery(
|
||
GET_LAXIMO_UNIT_DETAILS,
|
||
{
|
||
variables: selectedNode
|
||
? {
|
||
catalogCode: selectedNode.catalogCode || selectedNode.catalog || brand,
|
||
vehicleId: selectedNode.vehicleId || vehicleId,
|
||
unitId: selectedNode.unitid || selectedNode.unitId,
|
||
ssd: selectedNode.ssd || finalSsd || '',
|
||
}
|
||
: { catalogCode: '', vehicleId: '', unitId: '', ssd: '' },
|
||
skip: !selectedNode,
|
||
errorPolicy: 'all',
|
||
}
|
||
);
|
||
const unitDetails = unitDetailsData?.laximoUnitDetails || [];
|
||
|
||
// Логируем ошибки
|
||
if (vehicleError) {
|
||
console.error('Vehicle GraphQL error:', vehicleError);
|
||
}
|
||
|
||
if (vehicleLoading) {
|
||
return (
|
||
<>
|
||
<Head>
|
||
<title>Загрузка автомобиля...</title>
|
||
</Head>
|
||
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f9fafb' }}>
|
||
<div className="text-center">
|
||
<div className="animate-spin rounded-full h-32 w-32 border-b-2 border-red-600 mx-auto"></div>
|
||
<p className="mt-4 text-lg text-gray-600">Загружаем информацию об автомобиле...</p>
|
||
</div>
|
||
</div>
|
||
</>
|
||
);
|
||
}
|
||
|
||
// Если информация о каталоге недоступна, показываем ошибку
|
||
if (!catalogData?.laximoCatalogInfo) {
|
||
return (
|
||
<>
|
||
<Head>
|
||
<title>Каталог не найден</title>
|
||
</Head>
|
||
<main className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||
<div className="text-center">
|
||
<h1 className="text-2xl font-bold text-gray-900 mb-4">Каталог не найден</h1>
|
||
<p className="text-gray-600 mb-8">Информация о каталоге недоступна</p>
|
||
<button
|
||
onClick={() => router.back()}
|
||
className="bg-red-600 text-white px-6 py-3 rounded-lg hover:bg-red-700 transition-colors"
|
||
>
|
||
Назад к поиску
|
||
</button>
|
||
</div>
|
||
</main>
|
||
</>
|
||
);
|
||
}
|
||
|
||
// Если vehicleId отсутствует или пустой, показываем предупреждение
|
||
// Важно: vehicleId может быть '0' для некоторых автомобилей, найденных по VIN
|
||
if (!vehicleId || vehicleId === '') {
|
||
return (
|
||
<main className="min-h-screen bg-yellow-50 flex items-center justify-center">
|
||
<div className="text-center">
|
||
<h1 className="text-2xl font-bold text-yellow-900 mb-4">Автомобиль не выбран</h1>
|
||
<p className="text-yellow-700 mb-8">Для поиска по деталям необходимо выбрать конкретный автомобиль через VIN или мастер подбора.</p>
|
||
<button
|
||
onClick={() => router.back()}
|
||
className="bg-yellow-600 text-white px-6 py-3 rounded-lg hover:bg-yellow-700 transition-colors"
|
||
>
|
||
Назад к поиску
|
||
</button>
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|
||
|
||
// Гарантируем, что vehicleId — строка
|
||
const vehicleIdStr = Array.isArray(vehicleId) ? (vehicleId[0] || '') : (vehicleId || '');
|
||
// Для Laximo API vehicleId может быть '0' для автомобилей, найденных по VIN
|
||
const fallbackVehicleId = vehicleIdStr;
|
||
|
||
let vehicleInfo = vehicleData?.laximoVehicleInfo || {
|
||
vehicleid: fallbackVehicleId,
|
||
name: `Автомобиль ${catalogData.laximoCatalogInfo.name}`,
|
||
ssd: finalSsd,
|
||
brand: catalogData.laximoCatalogInfo.brand,
|
||
catalog: catalogData.laximoCatalogInfo.code,
|
||
attributes: [] as never[]
|
||
};
|
||
|
||
// Убеждаемся, что vehicleid соответствует параметру из URL
|
||
if (vehicleInfo.vehicleid !== fallbackVehicleId && fallbackVehicleId) {
|
||
vehicleInfo = { ...vehicleInfo, vehicleid: fallbackVehicleId };
|
||
}
|
||
|
||
|
||
|
||
// Если нет данных автомобиля и есть ошибка, показываем предупреждение
|
||
const hasError = vehicleError && !vehicleData?.laximoVehicleInfo;
|
||
const catalogInfo = catalogData.laximoCatalogInfo;
|
||
|
||
// Создаем динамические meta-теги
|
||
const vehicleName = vehicleInfo.brand && vehicleInfo.name
|
||
? (vehicleInfo.name.indexOf(vehicleInfo.brand) !== 0
|
||
? `${vehicleInfo.brand} ${vehicleInfo.name}`
|
||
: vehicleInfo.name)
|
||
: 'Автомобиль';
|
||
|
||
const metaData = {
|
||
title: `Запчасти для ${vehicleName} - Поиск по каталогу Protek`,
|
||
description: `Найдите и купите запчасти для ${vehicleName}. Широкий выбор оригинальных и аналоговых запчастей с быстрой доставкой.`,
|
||
keywords: `запчасти ${vehicleName}, ${vehicleInfo.brand} запчасти, автозапчасти, каталог запчастей`,
|
||
ogTitle: `Запчасти для ${vehicleName} - Protek`,
|
||
ogDescription: `Найдите и купите запчасти для ${vehicleName}. Широкий выбор оригинальных и аналоговых запчастей.`
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<MetaTags {...metaData} />
|
||
|
||
{/* ====== ВРЕМЕННЫЙ МАКЕТ ДЛЯ ВЕРСТКИ (начало) ====== */}
|
||
<InfoVin
|
||
vehicleName={
|
||
vehicleInfo.brand && vehicleInfo.name && vehicleInfo.name.indexOf(vehicleInfo.brand) !== 0
|
||
? `${vehicleInfo.brand} ${vehicleInfo.name}`
|
||
: vehicleInfo.name
|
||
}
|
||
vehicleInfo={
|
||
vehicleInfo.attributes && vehicleInfo.attributes.length > 0
|
||
? vehicleInfo.attributes.map(attr => attr.value).join(' · ')
|
||
: ''
|
||
}
|
||
vehicleAttributes={vehicleInfo.attributes || []}
|
||
/>
|
||
|
||
<div className="w-layout-blockcontainer container-vin w-container">
|
||
{!selectedNode ? (
|
||
<div className="w-layout-hflex flex-block-13">
|
||
{vehicleInfo && vehicleInfo.catalog && vehicleInfo.vehicleid && vehicleInfo.ssd && (
|
||
<>
|
||
<VinLeftbar
|
||
vehicleInfo={vehicleInfo}
|
||
onSearchResults={({ results, loading, error, query, isSearching }) => {
|
||
setFoundParts(results);
|
||
setSearchState({ loading, error, query, isSearching: isSearching || false });
|
||
}}
|
||
onNodeSelect={setSelectedNode}
|
||
onActiveTabChange={(tab) => setActiveTab(tab)}
|
||
onQuickGroupSelect={setSelectedQuickGroup}
|
||
activeTab={activeTab}
|
||
openedPath={openedPath}
|
||
setOpenedPath={setOpenedPath}
|
||
/>
|
||
{searchState.isSearching ? (
|
||
<div className="knot-parts">
|
||
{searchState.loading ? (
|
||
<div className="text-center py-12">
|
||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-red-600 mx-auto"></div>
|
||
<p className="mt-4 text-gray-600">Выполняется поиск...</p>
|
||
</div>
|
||
) : searchState.error ? (
|
||
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mt-3">
|
||
<div className="flex">
|
||
<div className="flex-shrink-0">
|
||
<svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
|
||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
|
||
</svg>
|
||
</div>
|
||
<div className="ml-3">
|
||
<h3 className="text-sm font-medium text-red-800">
|
||
Ошибка поиска
|
||
</h3>
|
||
<div className="mt-2 text-sm text-red-700">
|
||
<p>{searchState.error.message}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
) : foundParts.length > 0 ? (
|
||
foundParts.map((detail, idx) => (
|
||
<VinPartCard
|
||
key={detail.oem + idx}
|
||
n={idx + 1}
|
||
name={detail.name}
|
||
oem={detail.oem}
|
||
catalogCode={vehicleInfo.catalog}
|
||
vehicleId={vehicleInfo.vehicleid}
|
||
/>
|
||
))
|
||
) : (
|
||
<div className="text-center py-12">
|
||
<svg className="w-12 h-12 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||
</svg>
|
||
<p className="text-gray-600">
|
||
По запросу "{searchState.query}" ничего не найдено
|
||
</p>
|
||
<p className="text-sm text-gray-500 mt-1">
|
||
Попробуйте изменить поисковый запрос
|
||
</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : showKnot ? (
|
||
<VinKnot />
|
||
) : selectedQuickGroup ? (
|
||
<VinQuick
|
||
quickGroup={selectedQuickGroup}
|
||
catalogCode={vehicleInfo.catalog}
|
||
vehicleId={vehicleInfo.vehicleid}
|
||
ssd={vehicleInfo.ssd}
|
||
onBack={() => setSelectedQuickGroup(null)}
|
||
onNodeSelect={setSelectedNode}
|
||
/>
|
||
) : (
|
||
<VinCategory
|
||
catalogCode={vehicleInfo.catalog}
|
||
vehicleId={vehicleInfo.vehicleid}
|
||
ssd={vehicleInfo.ssd}
|
||
onNodeSelect={setSelectedNode}
|
||
activeTab={activeTab}
|
||
onQuickGroupSelect={setSelectedQuickGroup}
|
||
openedPath={openedPath}
|
||
setOpenedPath={setOpenedPath}
|
||
/>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
) : (
|
||
<div className="w-layout-hflex flex-block-13">
|
||
<div className="w-layout-vflex flex-block-14-copy-copy">
|
||
{/* <button onClick={() => setSelectedNode(null)} style={{ marginBottom: 16 }}>Назад</button> */}
|
||
{/* ОТЛАДКА: Логируем передачу SSD в KnotIn */}
|
||
{(() => {
|
||
const knotSsd = selectedNode.ssd || vehicleInfo.ssd;
|
||
console.log('🔍 [vehicleId].tsx передает в KnotIn:', {
|
||
selectedNodeSsd: selectedNode.ssd ? `${selectedNode.ssd.substring(0, 50)}...` : 'отсутствует',
|
||
vehicleInfoSsd: vehicleInfo.ssd ? `${vehicleInfo.ssd.substring(0, 50)}...` : 'отсутствует',
|
||
finalSsd: knotSsd ? `${knotSsd.substring(0, 50)}...` : 'отсутствует',
|
||
unitId: selectedNode.unitid,
|
||
unitName: selectedNode.name
|
||
});
|
||
return null;
|
||
})()}
|
||
<KnotIn
|
||
catalogCode={vehicleInfo.catalog}
|
||
vehicleId={vehicleInfo.vehicleid}
|
||
ssd={selectedNode.ssd || vehicleInfo.ssd} // ИСПРАВЛЕНИЕ: Используем SSD узла, fallback на родительский SSD
|
||
unitId={selectedNode.unitid}
|
||
unitName={selectedNode.name}
|
||
parts={unitDetails}
|
||
/>
|
||
{unitDetailsLoading ? (
|
||
<div style={{ padding: 24, textAlign: 'center' }}>Загружаем детали узла...</div>
|
||
) : unitDetailsError ? (
|
||
<div style={{ color: 'red', padding: 24 }}>Ошибка загрузки деталей: {unitDetailsError.message}</div>
|
||
) : unitDetails.length > 0 ? (
|
||
<KnotParts
|
||
parts={unitDetails}
|
||
catalogCode={vehicleInfo.catalog}
|
||
vehicleId={vehicleInfo.vehicleid}
|
||
/>
|
||
) : (
|
||
<div style={{ padding: 24, textAlign: 'center' }}>Детали не найдены</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
<section className="section-3">
|
||
<CatalogSubscribe />
|
||
</section>
|
||
<Footer />
|
||
<MobileMenuBottomSection />
|
||
|
||
{/* ====== ВРЕМЕННЫЙ МАКЕТ ДЛЯ ВЕРСТКИ (конец) ====== */}
|
||
|
||
{/* Навигация
|
||
<nav className="bg-white border-b">
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||
<div className="flex items-center justify-between h-16">
|
||
<div className="flex items-center space-x-4">
|
||
<button
|
||
onClick={() => router.back()}
|
||
className="text-gray-500 hover:text-gray-700 flex items-center space-x-2"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||
</svg>
|
||
<span>Назад</span>
|
||
</button>
|
||
<div className="text-sm text-gray-500">
|
||
<span>Главная</span>
|
||
<span className="mx-2">/</span>
|
||
<span>Каталог</span>
|
||
<span className="mx-2">/</span>
|
||
<span>{catalogInfo.name}</span>
|
||
<span className="mx-2">/</span>
|
||
<span className="text-gray-900 font-medium">{vehicleInfo.name}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
Информация об автомобиле
|
||
<div className="bg-white border-b">
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||
<div className="flex items-center space-x-4 mb-6">
|
||
{catalogInfo.icon && (
|
||
<img
|
||
src={`/images/brands/${catalogInfo.icon}`}
|
||
alt={catalogInfo.name}
|
||
className="w-12 h-12 object-contain"
|
||
onError={(e) => {
|
||
e.currentTarget.style.display = 'none';
|
||
}}
|
||
/>
|
||
)}
|
||
<div>
|
||
<h1 className="text-2xl font-bold text-gray-900">{vehicleInfo.name}</h1>
|
||
<p className="text-lg text-gray-600">{catalogInfo.name}</p>
|
||
</div>
|
||
</div>
|
||
|
||
Предупреждение об ошибке
|
||
{hasError && (
|
||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
|
||
<div className="flex">
|
||
<div className="flex-shrink-0">
|
||
<svg className="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor">
|
||
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
|
||
</svg>
|
||
</div>
|
||
<div className="ml-3">
|
||
<h3 className="text-sm font-medium text-yellow-800">
|
||
Предупреждение
|
||
</h3>
|
||
<div className="mt-2 text-sm text-yellow-700">
|
||
<p>Не удалось загрузить полную информацию об автомобиле. Отображается базовая информация.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
Отладочная информация
|
||
<div className="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-6">
|
||
<h4 className="text-sm font-medium text-gray-900 mb-3">
|
||
🔧 Отладочная информация
|
||
</h4>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
||
<div>
|
||
<span className="text-gray-500">Использовать localStorage:</span>
|
||
<span className="ml-2 font-medium">{useStorage ? 'Да' : 'Нет'}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-gray-500">Длина SSD из URL:</span>
|
||
<span className="ml-2 font-medium">{ssdLengthFromUrl || 'не указана'}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-gray-500">SSD получен:</span>
|
||
<span className="ml-2 font-medium">{finalSsd ? 'Да' : 'Нет'}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-gray-500">Длина SSD:</span>
|
||
<span className="ml-2 font-medium">{finalSsd.length}</span>
|
||
</div>
|
||
<div className="md:col-span-2">
|
||
<span className="text-gray-500">SSD (первые 100 символов):</span>
|
||
<span className="ml-2 font-mono text-xs break-all">
|
||
{finalSsd ? finalSsd.substring(0, 100) + '...' : 'отсутствует'}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
Характеристики автомобиля
|
||
{vehicleInfo.attributes && vehicleInfo.attributes.length > 0 && (
|
||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
||
{vehicleInfo.attributes.map((attr, index) => (
|
||
<div key={index} className="bg-gray-50 rounded-lg p-3">
|
||
<dt className="text-sm font-medium text-gray-500">{attr.name}</dt>
|
||
<dd className="text-sm text-gray-900 mt-1">{attr.value}</dd>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
Способы поиска запчастей
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||
<div className="mb-6">
|
||
<h2 className="text-xl font-bold text-gray-900 mb-2">Поиск запчастей</h2>
|
||
<p className="text-gray-600">
|
||
Выберите способ поиска запчастей для вашего автомобиля
|
||
</p>
|
||
</div>
|
||
|
||
Диагностический компонент
|
||
<LaximoDiagnostic
|
||
catalogCode={vehicleInfo.catalog}
|
||
vehicleId={vehicleInfo.vehicleid}
|
||
ssd={vehicleInfo.ssd}
|
||
/>
|
||
|
||
<VehiclePartsSearchSection
|
||
catalogInfo={catalogInfo}
|
||
vehicleInfo={vehicleInfo}
|
||
searchType={searchType}
|
||
onSearchTypeChange={setSearchType}
|
||
/>
|
||
</div> */}
|
||
</>
|
||
);
|
||
};
|
||
|
||
export default VehicleDetailsPage;
|