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

View File

@ -0,0 +1,603 @@
import React, { useState } from 'react';
import { useRouter } from 'next/router';
import { useQuery } from '@apollo/client';
import { GET_LAXIMO_QUICK_GROUPS, GET_LAXIMO_QUICK_DETAIL } from '@/lib/graphql';
import { LaximoQuickGroup, LaximoQuickDetail, LaximoUnit } from '@/types/laximo';
import BrandSelectionModal from './BrandSelectionModal';
import UnitDetailsSection from './UnitDetailsSection';
interface QuickGroupsSectionProps {
catalogCode: string;
vehicleId: string;
ssd?: string;
}
interface QuickGroupItemProps {
group: LaximoQuickGroup;
level: number;
onGroupClick: (group: LaximoQuickGroup) => void;
}
const QuickGroupItem: React.FC<QuickGroupItemProps> = ({ group, level, onGroupClick }) => {
const [isExpanded, setIsExpanded] = useState(false);
const hasChildren = group.children && group.children.length > 0;
const canShowDetails = group.link; // Только группы с link=true могут показывать детали
const handleGroupClick = () => {
if (canShowDetails) {
onGroupClick(group);
} else if (hasChildren) {
setIsExpanded(!isExpanded);
}
};
return (
<div className="w-full">
<div
onClick={handleGroupClick}
className={`
flex items-center justify-between p-3 border rounded-lg cursor-pointer transition-colors
${canShowDetails
? 'bg-white hover:bg-red-50 border-gray-200 hover:border-red-300'
: hasChildren
? 'bg-gray-50 hover:bg-gray-100 border-gray-200'
: 'bg-gray-100 border-gray-200 cursor-not-allowed'
}
${level > 0 ? 'ml-4' : ''}
`}
>
<div className="flex items-center space-x-3">
{hasChildren && (
<svg
className={`w-4 h-4 text-gray-400 transition-transform ${isExpanded ? 'rotate-90' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
)}
<div>
<h3 className={`font-medium ${canShowDetails ? 'text-gray-900' : 'text-gray-600'}`}>
{group.name}
</h3>
<p className="text-sm text-gray-500">
ID: {group.quickgroupid}
{canShowDetails && (
<span className="ml-2 inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">
Доступен поиск
</span>
)}
</p>
</div>
</div>
{canShowDetails && (
<svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
)}
</div>
{/* Дочерние группы */}
{hasChildren && isExpanded && (
<div className="mt-2 space-y-2">
{group.children!.map((childGroup) => (
<QuickGroupItem
key={childGroup.quickgroupid}
group={childGroup}
level={level + 1}
onGroupClick={onGroupClick}
/>
))}
</div>
)}
</div>
);
};
interface QuickDetailSectionProps {
catalogCode: string;
vehicleId: string;
selectedGroup: LaximoQuickGroup;
ssd: string;
onBack: () => void;
}
const QuickDetailSection: React.FC<QuickDetailSectionProps> = ({
catalogCode,
vehicleId,
selectedGroup,
ssd,
onBack
}) => {
console.log('🚀 QuickDetailSection рендерится с параметрами:', { catalogCode, vehicleId, selectedGroup, ssd });
const router = useRouter();
const [isBrandModalOpen, setIsBrandModalOpen] = useState(false);
const [selectedDetail, setSelectedDetail] = useState<any>(null);
const [expandedUnits, setExpandedUnits] = useState<Set<string>>(new Set());
const [selectedUnit, setSelectedUnit] = useState<LaximoUnit | null>(null);
const handleDetailClick = (detail: any) => {
const articleNumber = detail.oem;
console.log('🔍 Клик по детали из QuickGroups для выбора бренда:', { articleNumber, name: detail.name });
setSelectedDetail(detail);
setIsBrandModalOpen(true);
};
const handleCloseBrandModal = () => {
setIsBrandModalOpen(false);
setSelectedDetail(null);
};
const toggleUnitExpansion = (unitId: string) => {
const newExpanded = new Set(expandedUnits);
if (newExpanded.has(unitId)) {
newExpanded.delete(unitId);
} else {
newExpanded.add(unitId);
}
setExpandedUnits(newExpanded);
};
const handleUnitClick = (unit: LaximoUnit) => {
console.log('🔍 Выбран узел для детального просмотра:', unit.name, 'ID:', unit.unitid);
setSelectedUnit(unit);
};
const handleBackFromUnit = () => {
setSelectedUnit(null);
};
const { data: quickDetailData, loading: quickDetailLoading, error: quickDetailError } = useQuery<{ laximoQuickDetail: LaximoQuickDetail }>(
GET_LAXIMO_QUICK_DETAIL,
{
variables: {
catalogCode,
vehicleId,
quickGroupId: selectedGroup.quickgroupid,
ssd
},
skip: !catalogCode || !vehicleId || !selectedGroup.quickgroupid || !ssd,
errorPolicy: 'all',
fetchPolicy: 'cache-and-network' // Принудительно запрашиваем данные
}
);
const quickDetail = quickDetailData?.laximoQuickDetail;
// Добавляем отладочную информацию
console.log('🔍 QuickDetailSection Debug:');
console.log('📊 quickDetailData:', quickDetailData);
console.log('📋 quickDetail:', quickDetail);
console.log('🏗️ quickDetail.units:', quickDetail?.units);
console.log('⚙️ Variables:', { catalogCode, vehicleId, quickGroupId: selectedGroup.quickgroupid, ssd });
// Если выбран узел для детального просмотра, показываем UnitDetailsSection
if (selectedUnit) {
return (
<UnitDetailsSection
catalogCode={catalogCode}
vehicleId={vehicleId}
ssd={ssd}
unitId={selectedUnit.unitid}
unitName={selectedUnit.name}
onBack={handleBackFromUnit}
/>
);
}
if (quickDetailLoading) {
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<button
onClick={onBack}
className="flex items-center space-x-2 text-gray-600 hover:text-gray-900"
>
<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>
<div className="bg-white rounded-lg border p-6 text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-red-600 mx-auto"></div>
<p className="mt-2 text-gray-600">Загружаем детали...</p>
</div>
</div>
);
}
if (quickDetailError) {
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<button
onClick={onBack}
className="flex items-center space-x-2 text-gray-600 hover:text-gray-900"
>
<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>
<div className="bg-red-50 border border-red-200 rounded-lg p-6">
<h3 className="text-lg font-medium text-red-600 mb-2">Ошибка загрузки деталей</h3>
<p className="text-red-700">Не удалось загрузить детали для группы "{selectedGroup.name}"</p>
<p className="text-sm text-red-600 mt-2">Ошибка: {quickDetailError.message}</p>
</div>
</div>
);
}
return (
<div className="space-y-6">
{/* Навигация */}
<div className="flex items-center justify-between">
<button
onClick={onBack}
className="flex items-center space-x-2 text-gray-600 hover:text-gray-900"
>
<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">
Группа: {selectedGroup.quickgroupid}
</div>
</div>
{/* Заголовок */}
<div className="bg-white rounded-lg border p-6">
<h2 className="text-xl font-bold text-gray-900 mb-2">
{selectedGroup.name}
</h2>
<p className="text-gray-600">
Детали и узлы в группе быстрого поиска
</p>
</div>
{/* Детали */}
{quickDetail && quickDetail.units ? (
<div className="space-y-4">
{quickDetail.units.map((unit) => (
<div key={unit.unitid} className="bg-white rounded-lg border p-6">
<div className="flex items-start space-x-6 mb-4">
{/* Изображение узла */}
{(unit.imageurl || unit.largeimageurl) && (() => {
const finalImageUrl = unit.largeimageurl ? unit.largeimageurl.replace('%size%', '250') : unit.imageurl?.replace('%size%', '250') || '';
console.log('🖼️ Загружаем изображение:', finalImageUrl);
console.log('🔍 Raw URLs:', { imageurl: unit.imageurl, largeimageurl: unit.largeimageurl });
return (
<div className="flex-shrink-0">
<div className="text-xs text-gray-500 mb-2 p-2 bg-yellow-100 rounded">
Debug: {finalImageUrl}
</div>
<img
src={finalImageUrl}
alt={unit.name}
className="w-48 h-48 object-contain bg-gray-50 rounded-lg border border-gray-200 hover:border-red-300 transition-colors cursor-pointer"
onLoad={() => {
console.log('✅ Изображение загружено успешно:', finalImageUrl);
}}
onError={(e) => {
console.error('❌ Ошибка загрузки изображения:', finalImageUrl);
console.error('❌ Event:', e);
const img = e.target as HTMLImageElement;
img.style.border = '2px solid red';
img.alt = 'Ошибка загрузки';
}}
onClick={() => {
// Открываем изображение в новой вкладке
const imageUrl = unit.largeimageurl ? unit.largeimageurl.replace('%size%', '400') : unit.imageurl?.replace('%size%', '400') || '';
if (imageUrl) {
window.open(imageUrl, '_blank');
}
}}
/>
</div>
);
})()}
<div className="flex-1">
<div className="flex items-start justify-between mb-4">
<button
onClick={() => toggleUnitExpansion(unit.unitid)}
className="flex-1 text-left hover:bg-gray-50 p-2 rounded-lg transition-colors"
>
<div className="flex items-start justify-between">
<div className="flex-1">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
{unit.name}
{unit.details && unit.details.length > 0 && (
<svg
className={`w-5 h-5 ml-2 transform transition-transform ${
expandedUnits.has(unit.unitid) ? 'rotate-90' : ''
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
)}
</h3>
{unit.code && (
<p className="text-sm text-gray-500">Код: {unit.code}</p>
)}
{unit.details && unit.details.length > 0 && (
<p className="text-xs text-gray-400 mt-1">
{unit.details.length} деталей Нажмите для {expandedUnits.has(unit.unitid) ? 'скрытия' : 'показа'}
</p>
)}
</div>
<div className="flex flex-col space-y-2">
{unit.unitid && (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
ID: {unit.unitid}
</span>
)}
<button
onClick={(e) => {
e.stopPropagation(); // Предотвращаем всплытие события
handleUnitClick(unit);
}}
className="bg-green-600 hover:bg-green-700 text-white px-3 py-1 rounded text-sm font-medium transition-colors"
>
Подробнее
</button>
</div>
</div>
</button>
</div>
</div>
</div>
{unit.details && unit.details.length > 0 && expandedUnits.has(unit.unitid) && (
<div className="border-t pt-4">
<h4 className="text-sm font-medium text-gray-900 mb-3">Детали узла "{unit.name}":</h4>
<div className="space-y-3">
{unit.details.map((detail) => (
<div key={detail.detailid} className="p-4 bg-gray-50 rounded-lg border border-gray-200 hover:border-red-300 transition-colors">
<div className="flex items-start justify-between">
<div className="flex-1">
<h5 className="font-medium text-gray-900 mb-2">{detail.name}</h5>
<div className="space-y-1">
<p className="text-sm text-gray-600">
<span className="font-medium">OEM:</span>
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs font-mono ml-2">
{detail.oem}
</span>
</p>
{detail.brand && (
<p className="text-sm text-gray-600">
<span className="font-medium">Бренд:</span>
<span className="bg-green-100 text-green-800 px-2 py-1 rounded text-xs font-medium ml-2">
{detail.brand}
</span>
</p>
)}
{detail.note && (
<p className="text-sm text-gray-600">
<span className="font-medium">Примечание:</span> {detail.note}
</p>
)}
</div>
{detail.attributes && detail.attributes.length > 0 && (
<div className="mt-2 space-y-1">
{detail.attributes.map((attr, index) => (
<p key={index} className="text-xs text-gray-500">
<span className="font-medium">{attr.name || attr.key}:</span> {attr.value}
</p>
))}
</div>
)}
</div>
<div className="flex flex-col space-y-2 ml-4">
<button
onClick={() => handleDetailClick(detail)}
className="bg-red-600 hover:bg-red-700 text-white px-3 py-1 rounded text-sm font-medium transition-colors"
>
Найти предложения
</button>
<span className="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-gray-200 text-gray-800 text-center">
{detail.detailid}
</span>
</div>
</div>
</div>
))}
</div>
</div>
)}
</div>
))}
</div>
) : (
<div className="bg-white rounded-lg border p-6 text-center">
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2M4 13h2m8-8v2m0 6v2" />
</svg>
<h3 className="mt-2 text-sm font-medium text-gray-900">Нет доступных деталей</h3>
<p className="mt-1 text-sm text-gray-500">
В данной группе не найдено деталей или узлов.
</p>
</div>
)}
{selectedDetail && (
<BrandSelectionModal
isOpen={isBrandModalOpen}
onClose={handleCloseBrandModal}
articleNumber={selectedDetail.oem}
detailName={selectedDetail.name}
/>
)}
</div>
);
};
const QuickGroupsSection: React.FC<QuickGroupsSectionProps> = ({
catalogCode,
vehicleId,
ssd
}) => {
const [selectedGroup, setSelectedGroup] = useState<LaximoQuickGroup | null>(null);
// Получаем список групп быстрого поиска
const { data: quickGroupsData, loading: quickGroupsLoading, error: quickGroupsError } = useQuery<{ laximoQuickGroups: LaximoQuickGroup[] }>(
GET_LAXIMO_QUICK_GROUPS,
{
variables: {
catalogCode,
vehicleId,
...(ssd && ssd.trim() !== '' && { ssd })
},
skip: !catalogCode || !vehicleId,
errorPolicy: 'all'
}
);
const handleGroupClick = (group: LaximoQuickGroup) => {
if (!ssd || ssd.trim() === '') {
alert('Ошибка: Для поиска деталей необходимы данные автомобиля (SSD). Пожалуйста, выберите автомобиль заново.');
return;
}
console.log('🔍 Открываем детали группы быстрого поиска:', group.quickgroupid);
setSelectedGroup(group);
};
const handleBackToGroups = () => {
setSelectedGroup(null);
};
// Если выбрана группа для просмотра деталей
if (selectedGroup && ssd) {
return (
<QuickDetailSection
catalogCode={catalogCode}
vehicleId={vehicleId}
selectedGroup={selectedGroup}
ssd={ssd}
onBack={handleBackToGroups}
/>
);
}
if (quickGroupsLoading) {
return (
<div className="space-y-4">
<div className="bg-white rounded-lg border p-6">
<div className="animate-pulse space-y-4">
<div className="h-4 bg-gray-200 rounded w-1/4"></div>
<div className="space-y-2">
<div className="h-4 bg-gray-200 rounded"></div>
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
</div>
</div>
</div>
</div>
);
}
if (quickGroupsError) {
return (
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
<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>Не удалось загрузить группы быстрого поиска для данного автомобиля.</p>
<p className="mt-1">Ошибка: {quickGroupsError.message}</p>
</div>
</div>
</div>
</div>
);
}
const quickGroups = quickGroupsData?.laximoQuickGroups || [];
if (quickGroups.length === 0) {
return (
<div className="bg-white rounded-lg border p-6 text-center">
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
<h3 className="mt-2 text-sm font-medium text-gray-900">Группы быстрого поиска недоступны</h3>
<p className="mt-1 text-sm text-gray-500">
Для данного автомобиля не найдено групп быстрого поиска.
</p>
</div>
);
}
return (
<div className="space-y-4">
<div className="bg-white rounded-lg border p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-2">
Группы быстрого поиска
</h2>
<p className="text-gray-600 text-sm">
Выберите группу для поиска запчастей. Доступны только группы с активным поиском деталей.
</p>
</div>
<div className="space-y-3">
{quickGroups.map((group) => (
<QuickGroupItem
key={group.quickgroupid}
group={group}
level={0}
onGroupClick={handleGroupClick}
/>
))}
</div>
{/* Информационная панель */}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div className="flex">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-blue-800">
Информация о группах быстрого поиска
</h3>
<div className="mt-2 text-sm text-blue-700">
<ul className="list-disc list-inside space-y-1">
<li>Зеленая метка "Доступен поиск" указывает на возможность поиска деталей в группе</li>
<li>Группы без метки служат для организации структуры каталога</li>
<li>Нажмите на группу с активным поиском для просмотра деталей</li>
</ul>
</div>
</div>
</div>
</div>
</div>
);
};
export default QuickGroupsSection;