From 69ccc786ea111afba0b9a17d1ecdde602e54be74 Mon Sep 17 00:00:00 2001 From: egortriston Date: Mon, 30 Jun 2025 14:52:30 +0300 Subject: [PATCH] 1452 --- src/components/vin/VinLeftbar.tsx | 374 +++++++++++------- src/components/vin/VinPartCard.tsx | 41 +- .../vehicle-search/[brand]/[vehicleId].tsx | 104 +++-- src/styles/my.css | 9 + src/styles/protekproject.webflow.css | 2 +- 5 files changed, 336 insertions(+), 194 deletions(-) diff --git a/src/components/vin/VinLeftbar.tsx b/src/components/vin/VinLeftbar.tsx index 98199a1..9bd39dd 100644 --- a/src/components/vin/VinLeftbar.tsx +++ b/src/components/vin/VinLeftbar.tsx @@ -10,10 +10,23 @@ interface VinLeftbarProps { ssd: string; [key: string]: any; }; - onSearchResults?: (results: any[]) => void; + onSearchResults?: (data: { + results: any[]; + loading: boolean; + error: any; + query: string; + isSearching?: boolean; + }) => void; onNodeSelect?: (node: any) => void; } +interface QuickGroup { + quickgroupid: string; + name: string; + link?: boolean; + children?: QuickGroup[]; +} + const VinLeftbar: React.FC = ({ vehicleInfo, onSearchResults, onNodeSelect }) => { const catalogCode = vehicleInfo.catalog; const vehicleId = vehicleInfo.vehicleid; @@ -79,13 +92,15 @@ const VinLeftbar: React.FC = ({ vehicleInfo, onSearchResults, o const searchResults = data?.laximoFulltextSearch; useEffect(() => { - if (searchResults && onSearchResults) { - onSearchResults(searchResults.details || []); + if (onSearchResults) { + onSearchResults({ + results: searchResults?.details || [], + loading: loading, + error: error, + query: searchQuery + }); } - if (!searchQuery.trim() && onSearchResults) { - onSearchResults([]); - } - }, [searchResults, searchQuery, onSearchResults]); + }, [searchResults, loading, error, searchQuery, onSearchResults]); // --- Новый блок: вычисляем доступность поиска --- const isSearchAvailable = !!catalogCode && vehicleId !== undefined && vehicleId !== null && !!ssd && ssd.trim() !== ''; @@ -170,54 +185,64 @@ const VinLeftbar: React.FC = ({ vehicleInfo, onSearchResults, o }); const quickDetail = quickDetailData?.laximoQuickDetail; - const renderQuickGroupTree = (groups: any[], level = 0): React.ReactNode => ( -
- {groups.map(group => ( -
-
handleQuickGroupClick(group)} - > - {group.children && group.children.length > 0 && ( - - - - )} - {group.name} - {group.link && ( - Доступен поиск - )} -
- {group.children && group.children.length > 0 && expandedQuickGroups.has(group.quickgroupid) && ( -
- {renderQuickGroupTree(group.children, level + 1)} -
- )} -
- ))} -
- ); - // === Полнотекстовый поиск деталей (аналогично FulltextSearchSection) === const [fulltextQuery, setFulltextQuery] = useState(''); - const [executeFulltextSearch, { data: fulltextData, loading: fulltextLoading, error: fulltextError }] = useLazyQuery(SEARCH_LAXIMO_FULLTEXT, { errorPolicy: 'all' }); + const [executeFulltextSearch, { data: fulltextData, loading: fulltextLoading, error: fulltextError }] = useLazyQuery(GET_LAXIMO_FULLTEXT_SEARCH, { errorPolicy: 'all' }); const handleFulltextSearch = () => { - if (!fulltextQuery.trim()) return; + if (!fulltextQuery.trim()) { + if (onSearchResults) { + onSearchResults({ + results: [], + loading: false, + error: null, + query: '', + isSearching: false + }); + } + return; + } if (!ssd || ssd.trim() === '') { console.error('SSD обязателен для поиска по названию'); return; } + // Отправляем начальное состояние поиска родителю + if (onSearchResults) { + onSearchResults({ + results: [], + loading: true, + error: null, + query: fulltextQuery.trim(), + isSearching: true + }); + } executeFulltextSearch({ variables: { catalogCode, vehicleId, - searchText: fulltextQuery.trim(), + searchQuery: fulltextQuery.trim(), ssd } }); }; + const handleInputChange = (e: React.ChangeEvent) => { + const newValue = e.target.value; + setFulltextQuery(newValue); + }; + + useEffect(() => { + if (onSearchResults && (fulltextData || fulltextLoading || fulltextError)) { + onSearchResults({ + results: fulltextData?.laximoFulltextSearch?.details || [], + loading: fulltextLoading, + error: fulltextError, + query: fulltextQuery, + isSearching: true + }); + } + }, [fulltextData, fulltextLoading, fulltextError, onSearchResults]); + const handleFulltextKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault(); @@ -233,7 +258,7 @@ const VinLeftbar: React.FC = ({ vehicleInfo, onSearchResults, o
@@ -389,7 +348,6 @@ const VinLeftbar: React.FC = ({ vehicleInfo, onSearchResults, o <> {categories.map((category: any, idx: number) => { const isOpen = openIndex === idx; - // Подкатегории: сначала children, если нет — unitsByCategory const subcategories = category.children && category.children.length > 0 ? category.children : unitsByCategory[category.quickgroupid] || []; @@ -400,11 +358,11 @@ const VinLeftbar: React.FC = ({ vehicleInfo, onSearchResults, o data-delay="0" className={`dropdown-4 w-dropdown${isOpen ? " w--open" : ""}`} > -
handleToggle(idx, category.quickgroupid)} - style={{ cursor: "pointer" }} - > +
handleToggle(idx, category.quickgroupid)} + style={{ cursor: "pointer" }} + >
{category.name}
@@ -414,7 +372,7 @@ const VinLeftbar: React.FC = ({ vehicleInfo, onSearchResults, o
{ e.preventDefault(); if (onNodeSelect) { @@ -439,46 +397,158 @@ const VinLeftbar: React.FC = ({ vehicleInfo, onSearchResults, o ) ) : ( // Manufacturer tab content (QuickGroups) -
- {quickGroupsLoading ? ( -
Загружаем группы быстрого поиска...
- ) : quickGroupsError ? ( -
Ошибка загрузки групп: {quickGroupsError.message}
- ) : selectedQuickGroup ? ( -
- -

{selectedQuickGroup.name}

- {quickDetailLoading ? ( -
Загружаем детали...
- ) : quickDetailError ? ( -
Ошибка загрузки деталей: {quickDetailError.message}
- ) : quickDetail && quickDetail.units && quickDetail.units.length > 0 ? ( -
- {quickDetail.units.map((unit: any) => ( -
-
{unit.name}
- {unit.details && unit.details.length > 0 && ( -
    - {unit.details.map((detail: any) => ( -
  • - {detail.name} OEM: {detail.oem} -
  • - ))} -
- )} -
- ))} + quickGroupsLoading ? ( +
Загружаем группы быстрого поиска...
+ ) : quickGroupsError ? ( +
Ошибка загрузки групп: {quickGroupsError.message}
+ ) : ( + <> + {(quickGroups as QuickGroup[]).map((group: QuickGroup) => { + const hasChildren = group.children && group.children.length > 0; + const isOpen = expandedQuickGroups.has(group.quickgroupid); + + if (!hasChildren) { + return ( +
{ + e.preventDefault(); + if (group.link) { + handleQuickGroupClick(group); + } + }} + > + {group.name} + + ); + } + + return ( +
+
handleQuickGroupToggle(group.quickgroupid)} + style={{ cursor: "pointer" }} + > +
+
{group.name}
+
+
- ) : ( -
Нет деталей для этой группы
- )} -
- ) : quickGroups.length > 0 ? ( - renderQuickGroupTree(quickGroups) - ) : ( -
Нет доступных групп быстрого поиска
- )} -
+ ); + })} + + {/* Quick Detail Modal */} + {selectedQuickGroup && ( +
+
+
+

{selectedQuickGroup.name}

+ +
+ + {quickDetailLoading ? ( +
Загружаем детали...
+ ) : quickDetailError ? ( +
Ошибка загрузки деталей: {quickDetailError.message}
+ ) : quickDetail?.units?.length > 0 ? ( +
+ {quickDetail.units.map((unit: any) => ( +
+
{unit.name}
+ {unit.details && unit.details.length > 0 && ( +
+ {unit.details.map((detail: any) => ( +
+ {detail.name} + + OEM: {detail.oem} + +
+ ))} +
+ )} +
+ ))} +
+ ) : ( +
Нет деталей для этой группы
+ )} +
+
+ )} + + ) )} {/* Tab content end */}
diff --git a/src/components/vin/VinPartCard.tsx b/src/components/vin/VinPartCard.tsx index 1af1cae..8dbd165 100644 --- a/src/components/vin/VinPartCard.tsx +++ b/src/components/vin/VinPartCard.tsx @@ -1,5 +1,6 @@ -import React from "react"; +import React, { useState } from "react"; import { useRouter } from 'next/router'; +import BrandSelectionModal from '../BrandSelectionModal'; interface VinPartCardProps { n?: number; @@ -10,28 +11,38 @@ interface VinPartCardProps { const VinPartCard: React.FC = ({ n, oem, name, onPriceClick }) => { const router = useRouter(); + const [isBrandModalOpen, setIsBrandModalOpen] = useState(false); + const handlePriceClick = (e: React.MouseEvent) => { e.preventDefault(); if (onPriceClick) onPriceClick(); - if (oem) router.push(`/search?q=${encodeURIComponent(oem)}&mode=parts`); + setIsBrandModalOpen(true); }; return ( -
-
- {n !== undefined &&
{n}
} -
{oem}
-
-
{name}
-
- Цена -
- - - + <> +
+
+ {n !== undefined &&
{n}
} +
{oem}
+
+
{name}
+
+ Цена +
+ + + +
-
+ setIsBrandModalOpen(false)} + articleNumber={oem} + detailName={name} + /> + ); }; diff --git a/src/pages/vehicle-search/[brand]/[vehicleId].tsx b/src/pages/vehicle-search/[brand]/[vehicleId].tsx index f1cea15..3c6bbf5 100644 --- a/src/pages/vehicle-search/[brand]/[vehicleId].tsx +++ b/src/pages/vehicle-search/[brand]/[vehicleId].tsx @@ -53,6 +53,17 @@ const VehicleDetailsPage = () => { const [searchType, setSearchType] = useState<'quickgroups' | 'categories' | 'fulltext'>(defaultSearchType); const [showKnot, setShowKnot] = useState(false); const [foundParts, setFoundParts] = useState([]); + const [searchState, setSearchState] = useState<{ + loading: boolean; + error: any; + query: string; + isSearching: boolean; + }>({ + loading: false, + error: null, + query: '', + isSearching: false + }); const [selectedNode, setSelectedNode] = useState(null); const handleCategoryClick = (e?: React.MouseEvent) => { if (e) e.preventDefault(); @@ -272,33 +283,74 @@ const VehicleDetailsPage = () => { {!selectedNode ? (
{vehicleInfo && vehicleInfo.catalog && vehicleInfo.vehicleid && vehicleInfo.ssd && ( - - )} - {/* Категории или Knot или карточки */} - {foundParts.length > 0 ? ( -
- {foundParts.map((detail, idx) => ( - + { + setFoundParts(results); + setSearchState({ loading, error, query, isSearching: isSearching || false }); + }} + onNodeSelect={setSelectedNode} + /> + {searchState.isSearching ? ( +
+ {searchState.loading ? ( +
+
+

Выполняется поиск...

+
+ ) : searchState.error ? ( +
+
+
+ + + +
+
+

+ Ошибка поиска +

+
+

{searchState.error.message}

+
+
+
+
+ ) : foundParts.length > 0 ? ( + foundParts.map((detail, idx) => ( + + )) + ) : ( +
+ + + +

+ По запросу "{searchState.query}" ничего не найдено +

+

+ Попробуйте изменить поисковый запрос +

+
+ )} +
+ ) : showKnot ? ( + + ) : ( + - ))} -
- ) : showKnot ? ( - - ) : ( - + )} + )}
) : ( diff --git a/src/styles/my.css b/src/styles/my.css index 7492bcc..29130d9 100644 --- a/src/styles/my.css +++ b/src/styles/my.css @@ -465,4 +465,13 @@ input#VinSearchInput { grid-template-areas: none !important; grid-auto-flow: row !important; /* <--- ВАЖНО! */ } +} + + +.dropdown-toggle-card { + padding-left: 0 !important; +} + +.dropdown-link-3 { + margin-left: 0 !important; } \ No newline at end of file diff --git a/src/styles/protekproject.webflow.css b/src/styles/protekproject.webflow.css index ade85d5..ae467df 100644 --- a/src/styles/protekproject.webflow.css +++ b/src/styles/protekproject.webflow.css @@ -9486,7 +9486,7 @@ body { display: flex; } -.dropdown-toggle-3 { +.dropdown-toggle-3, .dropdown-toggle-card { border-top-right-radius: var(--_round---normal); border-bottom-right-radius: var(--_round---normal); border-left: 2px solid #0000;