import React, { useState, useEffect, useRef } from "react"; import { useRouter } from "next/router"; import { useLazyQuery } from '@apollo/client'; import BottomHead from "@/components/BottomHead"; import AuthModal from "@/components/auth/AuthModal"; import type { Client } from "@/types/auth"; import { useIsClient } from "@/lib/useIsomorphicLayoutEffect"; import { FIND_LAXIMO_VEHICLE, DOC_FIND_OEM, FIND_LAXIMO_VEHICLE_BY_PLATE_GLOBAL, FIND_LAXIMO_VEHICLES_BY_PART_NUMBER } from '@/lib/graphql'; import { LaximoVehicleSearchResult, LaximoDocFindOEMResult, LaximoVehiclesByPartResult } from '@/types/laximo'; import Link from "next/link"; import CartButton from './CartButton'; import SearchHistoryDropdown from './SearchHistoryDropdown'; import { GET_RECENT_SEARCH_QUERIES, PartsSearchHistoryItem } from '@/lib/graphql/search-history'; interface HeaderProps { onOpenAuthModal?: () => void; } const Header: React.FC = ({ onOpenAuthModal = () => console.log('Auth modal action not provided') }) => { const [menuOpen, setMenuOpen] = useState(false); const [currentUser, setCurrentUser] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [isSearching, setIsSearching] = useState(false); const [searchResults, setSearchResults] = useState([]); const [showResults, setShowResults] = useState(false); const [oemSearchResults, setOemSearchResults] = useState(null); const [vehiclesByPartResults, setVehiclesByPartResults] = useState(null); const [searchType, setSearchType] = useState<'vin' | 'oem' | 'plate' | 'text'>('text'); const [oemSearchMode, setOemSearchMode] = useState<'parts' | 'vehicles'>('parts'); const [showSearchHistory, setShowSearchHistory] = useState(false); const [searchHistoryItems, setSearchHistoryItems] = useState([]); const [inputFocused, setInputFocused] = useState(false); const [showPlaceholder, setShowPlaceholder] = useState(true); const router = useRouter(); const searchFormRef = useRef(null); const searchDropdownRef = useRef(null); const searchInputRef = useRef(null); const isClient = useIsClient(); // Эффект для восстановления поискового запроса из URL useEffect(() => { if (!router.isReady) return; // Если мы находимся на странице search-result, восстанавливаем поисковый запрос if (router.pathname === '/search-result') { const { article, brand } = router.query; if (article && typeof article === 'string') { // Отображаем только артикул, без бренда setSearchQuery(article); } } // Если мы находимся на странице search, восстанавливаем поисковый запрос else if (router.pathname === '/search') { const { q } = router.query; if (q && typeof q === 'string') { setSearchQuery(q); } } // Если мы находимся на странице vehicle-search-results, восстанавливаем поисковый запрос else if (router.pathname === '/vehicle-search-results') { const { q } = router.query; if (q && typeof q === 'string') { setSearchQuery(q); } } // Если мы находимся на странице деталей автомобиля, восстанавливаем VIN из URL else if (router.pathname === '/vehicle-search/[brand]/[vehicleId]') { const { vin } = router.query; if (vin && typeof vin === 'string') { setSearchQuery(vin); } else { setSearchQuery(''); } } // Для других страниц очищаем поисковый запрос else { setSearchQuery(''); } }, [router.isReady, router.pathname, router.query]); // Query для поиска по артикулу через Doc FindOEM const [findOEMParts] = useLazyQuery(DOC_FIND_OEM, { onCompleted: (data) => { const result = data.laximoDocFindOEM; console.log('🔍 Найдено деталей по артикулу:', result?.details?.length || 0); setOemSearchResults(result); setSearchResults([]); setIsSearching(false); setShowResults(true); }, onError: (error) => { console.error('❌ Ошибка поиска по артикулу:', error); setOemSearchResults(null); setSearchResults([]); setIsSearching(false); setShowResults(true); } }); // Query для поиска автомобилей по артикулу const [findVehiclesByPartNumber] = useLazyQuery(FIND_LAXIMO_VEHICLES_BY_PART_NUMBER, { onCompleted: (data) => { const result = data.laximoFindVehiclesByPartNumber; console.log('🔍 Найдено автомобилей по артикулу:', result?.totalVehicles || 0); setVehiclesByPartResults(result); setSearchResults([]); setOemSearchResults(null); setIsSearching(false); setShowResults(true); }, onError: (error) => { console.error('❌ Ошибка поиска автомобилей по артикулу:', error); setVehiclesByPartResults(null); setSearchResults([]); setOemSearchResults(null); setIsSearching(false); setShowResults(true); } }); // Запрос для получения истории поиска const [getSearchHistory, { loading: historyLoading }] = useLazyQuery(GET_RECENT_SEARCH_QUERIES, { onCompleted: (data) => { setSearchHistoryItems(data.partsSearchHistory?.items || []); }, onError: (error) => { console.error('❌ Ошибка загрузки истории поиска:', error); setSearchHistoryItems([]); } }); // Закрытие результатов при клике вне области useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (searchDropdownRef.current && !searchDropdownRef.current.contains(event.target as Node)) { setShowResults(false); setShowSearchHistory(false); setInputFocused(false); // Показываем placeholder обратно только если поле пустое if (searchQuery.trim() === '') { setShowPlaceholder(true); } } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); // Проверяем авторизацию при загрузке компонента (только на клиенте) useEffect(() => { if (!isClient) return; const token = localStorage.getItem('authToken'); const userData = localStorage.getItem('userData'); if (token && userData) { try { const user = JSON.parse(userData); setCurrentUser(user); } catch (error) { console.error('Ошибка парсинга данных пользователя:', error); localStorage.removeItem('authToken'); localStorage.removeItem('userData'); } } }, [isClient]); useEffect(() => { const bottomHead = document.querySelector('.bottom_head'); if (!bottomHead) return; const onScroll = () => { if (window.scrollY > 0) { bottomHead.classList.add('scrolled'); } else { bottomHead.classList.remove('scrolled'); } }; window.addEventListener('scroll', onScroll); return () => window.removeEventListener('scroll', onScroll); }, []); // Скрытие top_head при скролле // useEffect(() => { // const topHead = document.querySelector('.top_head'); // if (!topHead) return; // const onScroll = () => { // if (window.scrollY > 0) { // topHead.classList.add('hide-top-head'); // } else { // topHead.classList.remove('hide-top-head'); // } // }; // window.addEventListener('scroll', onScroll); // onScroll(); // return () => window.removeEventListener('scroll', onScroll); // }, []); // Проверяем, является ли строка VIN номером const isVinNumber = (query: string): boolean => { const cleanQuery = query.trim().toUpperCase(); // VIN состоит из 17 символов, содержит буквы и цифры, исключая I, O, Q return /^[A-HJ-NPR-Z0-9]{17}$/.test(cleanQuery); }; // Проверяем, является ли строка артикулом (OEM номером) const isOEMNumber = (query: string): boolean => { const cleanQuery = query.trim().toUpperCase(); // Артикул обычно содержит буквы и цифры, может содержать дефисы, точки // Длина от 3 до 20 символов, не должен быть VIN номером или госномером return /^[A-Z0-9\-\.]{3,20}$/.test(cleanQuery) && !isVinNumber(cleanQuery) && !isPlateNumber(cleanQuery); }; // Проверяем, является ли строка госномером РФ const isPlateNumber = (query: string): boolean => { const cleanQuery = query.trim().toUpperCase().replace(/\s+/g, ''); // Российские госномера: А123БВ77, А123БВ777, АА123А77, АА123А777, А123АА77, А123АА777 // Убираем пробелы и дефисы для проверки const platePatterns = [ /^[АВЕКМНОРСТУХ]\d{3}[АВЕКМНОРСТУХ]{2}\d{2,3}$/, // А123БВ77, А123БВ777 /^[АВЕКМНОРСТУХ]{2}\d{3}[АВЕКМНОРСТУХ]\d{2,3}$/, // АА123А77, АА123А777 /^[АВЕКМНОРСТУХ]\d{3}[АВЕКМНОРСТУХ]{2}\d{2,3}$/, // А123АА77, А123АА777 ]; return platePatterns.some(pattern => pattern.test(cleanQuery)); }; // Определяем тип поиска const getSearchType = (query: string): 'vin' | 'oem' | 'plate' | 'text' => { if (isVinNumber(query)) return 'vin'; if (isPlateNumber(query)) return 'plate'; if (isOEMNumber(query)) return 'oem'; return 'text'; }; // Список популярных каталогов для поиска по VIN const popularCatalogs = ['VW', 'AUDI', 'BMW', 'MERCEDES', 'FORD', 'TOYOTA', 'NISSAN', 'HYUNDAI', 'KIA']; // Обработчик поиска по VIN больше не используется (переходим на отдельную страницу) /* const handleVinSearch = async (vin: string) => { setIsSearching(true); setSearchResults([]); setOemSearchResults(null); setVehiclesByPartResults(null); console.log('🔍 Поиск по VIN глобально:', vin); // Выполняем глобальный поиск без указания каталога try { await findVehicleInCatalogs({ variables: { catalogCode: '', // Пустой код каталога для глобального поиска vin: vin } }); } catch (error) { console.error('❌ Ошибка глобального поиска по VIN:', error); } }; */ const handleOEMSearch = async (oemNumber: string) => { setIsSearching(true); setSearchResults([]); setOemSearchResults(null); setVehiclesByPartResults(null); console.log('🔍 Поиск по артикулу через Doc FindOEM:', oemNumber); try { await findOEMParts({ variables: { oemNumber: oemNumber.trim().toUpperCase() } }); } catch (error) { console.error('❌ Ошибка поиска по артикулу:', error); } }; // Обработчик поиска по госномеру больше не используется (переходим на отдельную страницу) /* const handlePlateSearch = async (plateNumber: string) => { setIsSearching(true); setSearchResults([]); setOemSearchResults(null); setVehiclesByPartResults(null); // Очищаем госномер от пробелов и приводим к верхнему регистру const cleanPlateNumber = plateNumber.trim().toUpperCase().replace(/\s+/g, ''); console.log('🔍 Поиск по госномеру:', cleanPlateNumber); try { await findVehicleByPlate({ variables: { plateNumber: cleanPlateNumber } }); } catch (error) { console.error('❌ Ошибка поиска по госномеру:', error); } }; */ const handlePartVehicleSearch = async (partNumber: string) => { setIsSearching(true); setSearchResults([]); setOemSearchResults(null); setVehiclesByPartResults(null); console.log('🔍 Поиск автомобилей по артикулу:', partNumber); try { await findVehiclesByPartNumber({ variables: { partNumber: partNumber.trim().toUpperCase() } }); } catch (error) { console.error('❌ Ошибка поиска автомобилей по артикулу:', error); } }; const handleSearchSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!searchQuery.trim()) return; const currentSearchType = getSearchType(searchQuery); setSearchType(currentSearchType); if (currentSearchType === 'vin') { // Переходим на страницу результатов поиска по VIN router.push(`/vehicle-search-results?q=${encodeURIComponent(searchQuery.trim().toUpperCase())}`); } else if (currentSearchType === 'plate') { // Переходим на страницу результатов поиска по госномеру router.push(`/vehicle-search-results?q=${encodeURIComponent(searchQuery.trim().toUpperCase())}`); } else if (currentSearchType === 'oem') { // Если это артикул, переходим на новую страницу поиска с режимом запчастей router.push(`/search?q=${encodeURIComponent(searchQuery.trim().toUpperCase())}&mode=parts`); } else { // Для текстового поиска также перенаправляем на новую страницу поиска router.push(`/search?q=${encodeURIComponent(searchQuery.trim())}&mode=parts`); } }; const handleVehicleSelect = (vehicle: LaximoVehicleSearchResult) => { setShowResults(false); // Переходим на страницу автомобиля - используем catalog вместо brand const catalogCode = (vehicle as any).catalog || vehicle.brand.toLowerCase(); console.log('🚗 Переход на страницу автомобиля:', { catalogCode, vehicleId: vehicle.vehicleid, ssd: vehicle.ssd }); // Создаем параметры URL const urlParams = new URLSearchParams(); // Добавляем SSD если есть if (vehicle.ssd) { urlParams.set('ssd', vehicle.ssd); } // Добавляем VIN-номер в URL, если поиск был по VIN if (searchType === 'vin' && searchQuery) { urlParams.set('vin', searchQuery); } // Если переход происходит из поиска автомобилей по артикулу, передаем артикул для автоматического поиска const currentOEMNumber = oemSearchMode === 'vehicles' ? searchQuery.trim().toUpperCase() : ''; if (currentOEMNumber) { urlParams.set('oemNumber', currentOEMNumber); } // Формируем URL const baseUrl = `/vehicle-search/${catalogCode}/${vehicle.vehicleid}`; const url = urlParams.toString() ? `${baseUrl}?${urlParams.toString()}` : baseUrl; // НЕ очищаем поисковый запрос, чтобы он остался в строке поиска // setSearchQuery(''); router.push(url); }; // Обработчик фокуса на поле ввода const handleInputFocus = () => { setInputFocused(true); setShowResults(false); setShowPlaceholder(false); if (searchQuery.trim() === '') { setShowSearchHistory(true); getSearchHistory({ variables: { limit: 5 } }); } }; // Обработчик изменения значения поля ввода const handleInputChange = (e: React.ChangeEvent) => { const value = e.target.value; setSearchQuery(value); // Управляем placeholder в зависимости от наличия текста if (value.trim() === '') { setShowPlaceholder(false); // Скрываем placeholder пока в фокусе setShowSearchHistory(true); setShowResults(false); getSearchHistory({ variables: { limit: 5 } }); } else { setShowPlaceholder(false); // Скрываем placeholder когда есть текст setShowSearchHistory(false); } }; // Обработчик потери фокуса const handleInputBlur = () => { // Показываем placeholder обратно только если поле пустое if (searchQuery.trim() === '') { setShowPlaceholder(true); } }; // Обработчик клика по элементу истории const handleHistoryItemClick = (searchQuery: string) => { setSearchQuery(searchQuery); setShowSearchHistory(false); setInputFocused(false); setShowPlaceholder(false); // Скрываем placeholder так как теперь есть текст // Фокусируем поле ввода для возможности редактирования if (searchInputRef.current) { searchInputRef.current.focus(); } }; return ( <> {/*
+7 (495) 260-20-60
*/}
setMenuOpen((open) => !open)} style={{ cursor: "pointer" }} >
searchFormRef.current?.requestSubmit()}>
{isSearching ? ( ) : ( )}
{/* История поиска */} {/* Результаты поиска VIN */} {showResults && searchResults.length > 0 && (searchType === 'vin' || searchType === 'plate') && (

{searchType === 'vin' ? 'Найденные автомобили по VIN' : 'Найденные автомобили по госномеру'}

{searchResults.map((vehicle, index) => ( ))}
)} {/* Результаты поиска по артикулу */} {showResults && searchType === 'oem' && (
{/* Переключатель режимов поиска */}

Поиск по артикулу: {searchQuery}

{/* Результаты поиска деталей */} {oemSearchMode === 'parts' && oemSearchResults && oemSearchResults.details.length > 0 && ( <>

Найдено {oemSearchResults.details.length} деталей

{oemSearchResults.details.slice(0, 5).map((detail, index) => (

{detail.name}

OEM: {detail.formattedoem}

Производитель: {detail.manufacturer}

{detail.replacements.length > 0 && (

+{detail.replacements.length} аналогов

)}
))} {oemSearchResults.details.length > 5 && (
)} )} {/* Результаты поиска автомобилей по артикулу */} {oemSearchMode === 'vehicles' && vehiclesByPartResults && vehiclesByPartResults.totalVehicles > 0 && ( <>

Найдено {vehiclesByPartResults.totalVehicles} автомобилей в {vehiclesByPartResults.catalogs.length} каталогах

{vehiclesByPartResults.catalogs.map((catalog, catalogIndex) => (

{catalog.brand} ({catalog.vehicleCount} автомобилей)

{catalog.vehicles.slice(0, 3).map((vehicle, vehicleIndex) => ( ))} {catalog.vehicles.length > 3 && (
)}
))} )} {/* Сообщения об отсутствии результатов */} {oemSearchMode === 'parts' && (!oemSearchResults || oemSearchResults.details.length === 0) && !isSearching && (

Детали не найдены

Детали с артикулом {searchQuery} не найдены в базе данных

)} {oemSearchMode === 'vehicles' && (!vehiclesByPartResults || vehiclesByPartResults.totalVehicles === 0) && !isSearching && (

Автомобили не найдены

Автомобили с артикулом {searchQuery} не найдены в каталогах

)}
)} {/* Сообщение о том, что VIN/госномер не найден */} {showResults && searchResults.length === 0 && (searchType === 'vin' || searchType === 'plate') && !isSearching && (

{searchType === 'vin' ? 'VIN не найден' : 'Госномер не найден'}

{searchType === 'vin' ? `Автомобиль с VIN ${searchQuery} не найден в доступных каталогах` : `Автомобиль с госномером ${searchQuery} не найден в базе данных` }

)}
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
История заказов
Добавить в гараж
Избранное
setMenuOpen(false)} /> ); }; export default Header;