first commit

This commit is contained in:
Bivekich
2025-06-26 06:59:59 +03:00
commit d44874775c
450 changed files with 76635 additions and 0 deletions

314
src/pages/search.tsx Normal file
View File

@ -0,0 +1,314 @@
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { useQuery } from '@apollo/client';
import Head from 'next/head';
import Header from '@/components/Header';
import Footer from '@/components/Footer';
import MobileMenuBottomSection from '@/components/MobileMenuBottomSection';
import { DOC_FIND_OEM, FIND_LAXIMO_VEHICLES_BY_PART_NUMBER } from '@/lib/graphql';
import { LaximoDocFindOEMResult, LaximoVehiclesByPartResult, LaximoVehicleSearchResult } from '@/types/laximo';
type SearchMode = 'parts' | 'vehicles';
const InfoSearch = () => (
<section className="section-info">
<div className="w-layout-blockcontainer container info w-container">
<div className="w-layout-vflex flex-block-9">
<div className="w-layout-hflex flex-block-7">
<a href="/" className="link-block w-inline-block">
<div>Главная</div>
</a>
<div className="text-block-3"></div>
<a href="#" className="link-block-2 w-inline-block">
<div>Поиск деталей по артикулу</div>
</a>
</div>
<div className="w-layout-hflex flex-block-8">
<div className="w-layout-hflex flex-block-10">
<h1 className="heading">Поиск деталей по артикулу</h1>
</div>
</div>
</div>
</div>
</section>
);
const SearchPage = () => {
const router = useRouter();
const { q, mode } = router.query;
const [searchQuery, setSearchQuery] = useState<string>("");
const [searchMode, setSearchMode] = useState<SearchMode>('parts');
useEffect(() => {
if (q && typeof q === 'string') {
setSearchQuery(q.trim().toUpperCase());
}
if (mode && typeof mode === 'string' && (mode === 'parts' || mode === 'vehicles')) {
setSearchMode(mode);
}
}, [q, mode]);
const { data: partsData, loading: partsLoading, error: partsError } = useQuery(DOC_FIND_OEM, {
variables: { oemNumber: searchQuery },
skip: !searchQuery || searchMode !== 'parts',
errorPolicy: 'all'
});
const { data: vehiclesData, loading: vehiclesLoading, error: vehiclesError } = useQuery(FIND_LAXIMO_VEHICLES_BY_PART_NUMBER, {
variables: { partNumber: searchQuery },
skip: !searchQuery || searchMode !== 'vehicles',
errorPolicy: 'all'
});
const handleSearchModeChange = (mode: SearchMode) => {
setSearchMode(mode);
if (searchQuery) {
router.push(`/search?q=${encodeURIComponent(searchQuery)}&mode=${mode}`, undefined, { shallow: true });
}
};
const handleSearchSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!searchQuery.trim()) return;
router.push(`/search?q=${encodeURIComponent(searchQuery.trim().toUpperCase())}&mode=${searchMode}`);
};
const handlePartDetail = (detail: any) => {
router.push(`/search-result?article=${encodeURIComponent(detail.formattedoem)}&brand=${encodeURIComponent(detail.manufacturer)}`);
};
const handleVehicleSelect = (vehicle: LaximoVehicleSearchResult) => {
const vehicleBrand = vehicle.brand || vehicle.name?.split(' ')[0] || 'UNKNOWN';
const url = `/search-result?article=${encodeURIComponent(searchQuery)}&brand=${encodeURIComponent(vehicleBrand)}`;
router.push(url);
};
const handleShowAllVehicles = (catalogCode?: string) => {
const url = catalogCode
? `/vehicles-by-part?partNumber=${encodeURIComponent(searchQuery)}&catalogCode=${catalogCode}`
: `/vehicles-by-part?partNumber=${encodeURIComponent(searchQuery)}`;
router.push(url);
};
const isLoading = (searchMode === 'parts' && partsLoading) || (searchMode === 'vehicles' && vehiclesLoading);
const hasError = (searchMode === 'parts' && partsError) || (searchMode === 'vehicles' && vehiclesError);
const partsResult: LaximoDocFindOEMResult | null = partsData?.laximoDocFindOEM || null;
const vehiclesResult: LaximoVehiclesByPartResult | null = vehiclesData?.laximoFindVehiclesByPartNumber || null;
const hasPartsResults = partsResult && partsResult.details && partsResult.details.length > 0;
const hasVehiclesResults = vehiclesResult && vehiclesResult.totalVehicles > 0;
return (
<>
<Head>
<title>Поиск по артикулу {searchQuery} - Protek</title>
<meta name="description" content={`Результаты поиска по артикулу ${searchQuery}`} />
<link href="images/favicon.png" rel="shortcut icon" type="image/x-icon" />
<link href="images/webclip.png" rel="apple-touch-icon" />
</Head>
<InfoSearch />
<div className="page-wrapper bg-[#F5F8FB] min-h-screen">
<div className="flex flex-col pt-10 pb-16 max-md:px-5">
<div className="flex flex-col items-center w-full">
<div className="w-full max-w-[1580px]">
{/* Переключатель режима поиска */}
{/* {searchQuery && (
<div className="bg-white rounded-2xl shadow p-6 mb-6 flex flex-col items-center">
<div className="flex space-x-1 bg-gray-100 rounded-lg p-1 w-full max-w-md">
<button
onClick={() => handleSearchModeChange('parts')}
className={`flex-1 px-4 py-2 text-sm font-medium rounded-md transition-colors ${
searchMode === 'parts'
? 'bg-white text-[#EC1C24] shadow-sm'
: 'text-gray-600 hover:text-gray-900'
}`}
>
🔧 Найти запчасти
</button>
<button
onClick={() => handleSearchModeChange('vehicles')}
className={`flex-1 px-4 py-2 text-sm font-medium rounded-md transition-colors ${
searchMode === 'vehicles'
? 'bg-white text-[#EC1C24] shadow-sm'
: 'text-gray-600 hover:text-gray-900'
}`}
>
🚗 Найти автомобили
</button>
</div>
</div>
)} */}
{/* Обработка ошибок */}
{searchQuery && hasError && (
<div className="bg-red-50 border border-red-200 rounded-2xl shadow p-10 mb-6">
<div className="flex items-center">
<svg className="w-6 h-6 text-red-600 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<div>
<h3 className="text-lg font-medium text-red-800">Ошибка поиска</h3>
<p className="text-red-700 mt-1">Произошла ошибка при поиске. Попробуйте еще раз.</p>
</div>
</div>
</div>
)}
{/* Загрузка */}
{searchQuery && isLoading && (
<div className="bg-white rounded-2xl shadow p-10 flex flex-col items-center justify-center min-h-[300px]">
<div className="animate-spin rounded-full h-24 w-24 border-b-2 border-red-600 mb-6"></div>
<p className="text-lg text-gray-600">Поиск по артикулу...</p>
</div>
)}
{/* Результаты поиска */}
{searchQuery && !isLoading && !hasError && (
<div className="space-y-6">
{/* Результаты поиска запчастей */}
{searchMode === 'parts' && (
<>
{!hasPartsResults && (
<div className="bg-[#eaf0fa] border border-[#b3c6e6] rounded-2xl shadow p-10 text-center">
<svg className="w-16 h-16 mx-auto mb-4" style={{ color: '#0d336c' }} 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 5.291A7.962 7.962 0 0112 15c-2.34 0-4.29-1.009-5.824-2.562M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<h3 className="text-xl font-semibold mb-2" style={{ color: '#0d336c' }}>
Детали не найдены
</h3>
<p className="mb-4" style={{ color: '#0d336c' }}>
По артикулу <span className="font-mono font-semibold">{searchQuery}</span> детали не найдены.
</p>
<p className="text-sm" style={{ color: '#3b5a99' }}>
Попробуйте изменить запрос или проверьте правильность написания артикула.
</p>
</div>
)}
{hasPartsResults && (
<div className="bg-white rounded-2xl shadow p-10">
<div className="border-b border-gray-200 pb-4">
<h2 className="text-xl font-semibold text-gray-900">
Поиск деталей по артикулу: {searchQuery}
</h2>
<p className="text-sm text-gray-600 mt-1">
Выберите нужную деталь
</p>
</div>
<div className="divide-y divide-gray-200">
{partsResult!.details.map((detail, index) => (
<div key={detail.detailid || index}>
<button
onClick={() => handlePartDetail(detail)}
className="w-full text-left p-4 hover:bg-gray-50 transition-colors block group"
>
<div className="flex w-full items-center gap-2">
<div className="w-1/5 max-md:w-1/3 font-bold text-left truncate" style={{ color: 'rgb(77, 180, 94)' }}>{detail.manufacturer}</div>
<div className="w-1/5 max-md:text-center max-md:w-1/3 font-bold text-left truncate group-hover:text-[#EC1C24] transition-colors">{detail.formattedoem || detail.oem}</div>
<div className="w-3/5 max-md:w-1/3 text-left truncate">{detail.name}</div>
</div>
</button>
</div>
))}
</div>
</div>
)}
</>
)}
{/* Результаты поиска автомобилей */}
{searchMode === 'vehicles' && (
<div className="bg-white rounded-2xl shadow p-10">
<div className="border-b border-gray-200 pb-4 mb-4 flex items-center justify-between">
<h2 className="text-xl font-semibold text-gray-900">
Поиск автомобилей по артикулу: {searchQuery}
</h2>
{hasVehiclesResults && (
<span className="text-sm text-gray-600">
Найдено {vehiclesResult!.totalVehicles} автомобилей в {vehiclesResult!.catalogs.length} каталогах
</span>
)}
</div>
{hasVehiclesResults ? (
<>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Бренд
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Артикул
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Наименование
</th>
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
Рынок
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{vehiclesResult!.catalogs.map((catalog) => (
<tr
key={catalog.catalogCode}
className="hover:bg-gray-50 cursor-pointer transition-colors"
onClick={() => {
router.push(`/search-result?article=${encodeURIComponent(searchQuery)}&brand=${encodeURIComponent(catalog.brand)}`);
}}
>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<div className="text-sm font-medium text-gray-900">
{catalog.brand}
</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-900">{searchQuery}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-900">ГАЕЧНЫЙ КЛЮЧ</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm text-gray-500">
{catalog.catalogCode}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="pt-6">
<button
onClick={() => handleShowAllVehicles()}
className="w-full bg-[#EC1C24] text-white py-3 px-6 rounded-lg hover:bg-[#b91c1c] transition-colors font-medium"
>
Показать все {vehiclesResult!.totalVehicles} автомобилей
</button>
</div>
</>
) : (
<div className="bg-yellow-50 border border-yellow-200 rounded-2xl shadow p-10 text-center">
<svg className="w-16 h-16 text-yellow-600 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.728-.833-2.498 0L4.316 14.5c-.77.833.192 2.5 1.732 2.5z" />
</svg>
<h3 className="text-xl font-semibold text-yellow-800 mb-2">Автомобили не найдены</h3>
<p className="text-yellow-700 mb-4">Автомобили с артикулом {searchQuery} не найдены в каталогах</p>
</div>
)}
</div>
)}
</div>
)}
</div>
</div>
</div>
<MobileMenuBottomSection />
<Footer />
</div>
</>
);
};
export default SearchPage;