Compare commits
6 Commits
cartandfav
...
4a3da4d5c5
Author | SHA1 | Date | |
---|---|---|---|
4a3da4d5c5 | |||
f894b7e023 | |||
a879e5e5e7 | |||
85f7634158 | |||
d62db55160 | |||
5e454a7367 |
@ -52,7 +52,7 @@ const BrandSelectionModal: React.FC<BrandSelectionModalProps> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"
|
||||
className="fixed inset-0 bg-black/10 bg-opacity-50 flex items-center justify-center z-50 p-4"
|
||||
onClick={handleBackdropClick}
|
||||
>
|
||||
<div className="bg-white rounded-lg shadow-xl max-w-md w-full max-h-[80vh] overflow-hidden">
|
||||
|
@ -208,7 +208,7 @@ const CatalogGroupsSection: React.FC<CatalogGroupsSectionProps> = ({
|
||||
vehicleId,
|
||||
...(ssd && ssd.trim() !== '' && { ssd })
|
||||
},
|
||||
skip: !catalogCode || !vehicleId || catalogType !== 'quickGroups',
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || catalogType !== 'quickGroups',
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
);
|
||||
|
@ -33,7 +33,7 @@ const CategoriesSection: React.FC<CategoriesSectionProps> = ({
|
||||
vehicleId,
|
||||
...(ssd && ssd.trim() !== '' && { ssd })
|
||||
},
|
||||
skip: !catalogCode || !vehicleId,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null,
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
);
|
||||
|
@ -187,13 +187,13 @@ const GroupDetailsSection: React.FC<GroupDetailsSectionProps> = ({
|
||||
const { data, loading, error } = useQuery<{ laximoQuickDetail: LaximoQuickDetail }>(
|
||||
GET_LAXIMO_QUICK_DETAIL,
|
||||
{
|
||||
variables: {
|
||||
variables: quickGroupId ? {
|
||||
catalogCode,
|
||||
vehicleId,
|
||||
quickGroupId,
|
||||
ssd
|
||||
},
|
||||
skip: !catalogCode || !vehicleId || !quickGroupId || !ssd,
|
||||
} : undefined,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || !quickGroupId || !ssd || ssd.trim() === '',
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
);
|
||||
|
@ -30,7 +30,7 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
onSuccess={handleAuthSuccess}
|
||||
/>
|
||||
</header>
|
||||
<main className="pt-[132px]">{children}</main>
|
||||
<main className="pt-[108px] md:pt-[131px]">{children}</main>
|
||||
<MobileMenuBottomSection onOpenAuthModal={() => setAuthModalOpen(true)} />
|
||||
</>
|
||||
);
|
||||
|
@ -154,13 +154,13 @@ const QuickDetailSection: React.FC<QuickDetailSectionProps> = ({
|
||||
const { data: quickDetailData, loading: quickDetailLoading, error: quickDetailError } = useQuery<{ laximoQuickDetail: LaximoQuickDetail }>(
|
||||
GET_LAXIMO_QUICK_DETAIL,
|
||||
{
|
||||
variables: {
|
||||
variables: selectedGroup?.quickgroupid ? {
|
||||
catalogCode,
|
||||
vehicleId,
|
||||
quickGroupId: selectedGroup.quickgroupid,
|
||||
ssd
|
||||
},
|
||||
skip: !catalogCode || !vehicleId || !selectedGroup.quickgroupid || !ssd,
|
||||
} : undefined,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || !selectedGroup?.quickgroupid || !ssd || ssd.trim() === '',
|
||||
errorPolicy: 'all',
|
||||
fetchPolicy: 'cache-and-network' // Принудительно запрашиваем данные
|
||||
}
|
||||
@ -169,11 +169,28 @@ const QuickDetailSection: React.FC<QuickDetailSectionProps> = ({
|
||||
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 });
|
||||
console.log('🔍 QuickDetailSection Debug:', {
|
||||
catalogCode,
|
||||
vehicleId,
|
||||
vehicleIdType: typeof vehicleId,
|
||||
quickGroupId: selectedGroup?.quickgroupid,
|
||||
quickGroupIdType: typeof selectedGroup?.quickgroupid,
|
||||
ssd: ssd ? `${ssd.substring(0, 30)}...` : 'отсутствует',
|
||||
ssdType: typeof ssd,
|
||||
ssdLength: ssd?.length,
|
||||
hasData: !!quickDetailData,
|
||||
hasQuickDetail: !!quickDetail,
|
||||
unitsCount: quickDetail?.units?.length || 0,
|
||||
loading: quickDetailLoading,
|
||||
error: quickDetailError?.message,
|
||||
skipCondition: !catalogCode || vehicleId === undefined || vehicleId === null || !selectedGroup?.quickgroupid || !ssd,
|
||||
skipDetails: {
|
||||
noCatalogCode: !catalogCode,
|
||||
noVehicleId: vehicleId === undefined || vehicleId === null,
|
||||
noQuickGroupId: !selectedGroup?.quickgroupid,
|
||||
noSsd: !ssd
|
||||
}
|
||||
});
|
||||
|
||||
// Если выбран узел для детального просмотра, показываем UnitDetailsSection
|
||||
if (selectedUnit) {
|
||||
@ -213,6 +230,20 @@ const QuickDetailSection: React.FC<QuickDetailSectionProps> = ({
|
||||
}
|
||||
|
||||
if (quickDetailError) {
|
||||
console.error('🚨 QuickDetailSection Error Details:', {
|
||||
message: quickDetailError.message,
|
||||
graphQLErrors: quickDetailError.graphQLErrors,
|
||||
networkError: quickDetailError.networkError,
|
||||
extraInfo: quickDetailError.extraInfo,
|
||||
selectedGroup: selectedGroup,
|
||||
variables: selectedGroup?.quickgroupid ? {
|
||||
catalogCode,
|
||||
vehicleId,
|
||||
quickGroupId: selectedGroup.quickgroupid,
|
||||
ssd
|
||||
} : 'undefined (no variables sent)'
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
@ -231,6 +262,33 @@ const QuickDetailSection: React.FC<QuickDetailSectionProps> = ({
|
||||
<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>
|
||||
|
||||
{/* Отладочная информация */}
|
||||
<details className="mt-4">
|
||||
<summary className="text-sm text-red-700 cursor-pointer hover:text-red-800">
|
||||
🔧 Показать отладочную информацию
|
||||
</summary>
|
||||
<div className="mt-2 p-3 bg-red-100 rounded text-xs">
|
||||
<div><strong>Catalog Code:</strong> {catalogCode}</div>
|
||||
<div><strong>Vehicle ID:</strong> {vehicleId} (type: {typeof vehicleId})</div>
|
||||
<div><strong>Quick Group ID:</strong> {selectedGroup?.quickgroupid} (type: {typeof selectedGroup?.quickgroupid})</div>
|
||||
<div><strong>SSD:</strong> {ssd ? `${ssd.substring(0, 100)}...` : 'отсутствует'} (length: {ssd?.length})</div>
|
||||
<div className="mt-2">
|
||||
<strong>GraphQL Errors:</strong>
|
||||
<pre className="mt-1 text-xs overflow-auto">
|
||||
{JSON.stringify(quickDetailError.graphQLErrors, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
{quickDetailError.networkError && (
|
||||
<div className="mt-2">
|
||||
<strong>Network Error:</strong>
|
||||
<pre className="mt-1 text-xs overflow-auto">
|
||||
{JSON.stringify(quickDetailError.networkError, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -464,7 +522,7 @@ const QuickGroupsSection: React.FC<QuickGroupsSectionProps> = ({
|
||||
vehicleId,
|
||||
...(ssd && ssd.trim() !== '' && { ssd })
|
||||
},
|
||||
skip: !catalogCode || !vehicleId,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null,
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
);
|
||||
|
@ -39,7 +39,7 @@ const UnitDetailsSection: React.FC<UnitDetailsSectionProps> = ({
|
||||
unitId,
|
||||
ssd: ssd || ''
|
||||
},
|
||||
skip: !catalogCode || !vehicleId || !unitId,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || !unitId,
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
);
|
||||
@ -54,7 +54,7 @@ const UnitDetailsSection: React.FC<UnitDetailsSectionProps> = ({
|
||||
unitId,
|
||||
ssd: ssd || ''
|
||||
},
|
||||
skip: !catalogCode || !vehicleId || !unitId,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || !unitId,
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
);
|
||||
@ -69,7 +69,7 @@ const UnitDetailsSection: React.FC<UnitDetailsSectionProps> = ({
|
||||
unitId,
|
||||
ssd: ssd || ''
|
||||
},
|
||||
skip: !catalogCode || !vehicleId || !unitId,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || !unitId,
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
);
|
||||
|
@ -59,7 +59,7 @@ const UnitsSection: React.FC<UnitsSectionProps> = ({
|
||||
categoryId,
|
||||
...(ssd && ssd.trim() !== '' && { ssd })
|
||||
},
|
||||
skip: !catalogCode || !vehicleId || !categoryId,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || !categoryId,
|
||||
errorPolicy: 'all',
|
||||
fetchPolicy: 'no-cache', // Полностью отключаем кэширование для гарантии свежих данных
|
||||
notifyOnNetworkStatusChange: true
|
||||
|
@ -210,7 +210,7 @@ const VehiclePartsSearchSection: React.FC<VehiclePartsSearchSectionProps> = ({
|
||||
<div className="min-h-[400px]">
|
||||
{searchType === 'quickgroups' && supportsQuickGroups && (
|
||||
<QuickGroupsSection
|
||||
catalogCode={vehicleInfo.catalog}
|
||||
catalogCode={vehicleInfo.catalog}
|
||||
vehicleId={vehicleInfo.vehicleid}
|
||||
ssd={vehicleInfo.ssd}
|
||||
/>
|
||||
|
@ -110,7 +110,7 @@ const LegalEntityListBlock: React.FC<LegalEntityListBlockProps> = ({ legalEntiti
|
||||
</div>
|
||||
<div
|
||||
layer-name="link_control_element"
|
||||
className="flex relative gap-1.5 items-center cursor-pointer hover:text-red-600"
|
||||
className="flex relative gap-1.5 items-center cursor-pointer group"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => router.push('/profile-requisites')}
|
||||
@ -130,7 +130,7 @@ const LegalEntityListBlock: React.FC<LegalEntityListBlockProps> = ({ legalEntiti
|
||||
</div>
|
||||
<div
|
||||
layer-name="Редактировать"
|
||||
className="text-sm leading-5 text-gray-600"
|
||||
className="text-sm leading-5 text-gray-600 group-hover:text-red-600"
|
||||
>
|
||||
Реквизиты компании
|
||||
</div>
|
||||
@ -141,8 +141,9 @@ const LegalEntityListBlock: React.FC<LegalEntityListBlockProps> = ({ legalEntiti
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="flex relative gap-1.5 items-center cursor-pointer hover:text-red-600"
|
||||
className="flex relative gap-1.5 items-center cursor-pointer group"
|
||||
onClick={() => onEdit && onEdit(entity)}
|
||||
aria-label="Редактировать юридическое лицо"
|
||||
>
|
||||
<div className="relative h-4 w-[18px]">
|
||||
<Image
|
||||
@ -153,26 +154,37 @@ const LegalEntityListBlock: React.FC<LegalEntityListBlockProps> = ({ legalEntiti
|
||||
className="absolute left-0.5 top-0"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm leading-5 text-gray-600">
|
||||
<div className="text-sm leading-5 text-gray-600 group-hover:text-red-600">
|
||||
Редактировать
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="flex relative gap-1.5 items-center cursor-pointer hover:text-red-600"
|
||||
className="flex relative gap-1.5 items-center cursor-pointer group"
|
||||
aria-label="Удалить юридическое лицо"
|
||||
onClick={() => handleDelete(entity.id, entity.shortName)}
|
||||
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && handleDelete(entity.id, entity.shortName)}
|
||||
style={{ display: 'inline-flex', cursor: 'pointer', transition: 'color 0.2s' }}
|
||||
onMouseEnter={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#ec1c24');
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#D0D0D0');
|
||||
}}
|
||||
>
|
||||
<div className="relative h-4 w-[18px]">
|
||||
<Image
|
||||
src="/images/delete.svg"
|
||||
alt="Удалить"
|
||||
width={16}
|
||||
height={16}
|
||||
className="absolute left-0.5 top-0"
|
||||
/>
|
||||
<div className="relative h-4 w-4">
|
||||
<svg width="16" height="16" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"
|
||||
fill="#D0D0D0"
|
||||
style={{ transition: 'fill 0.2s' }}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-sm leading-5 text-gray-600">
|
||||
<div className="text-sm leading-5 text-gray-600 group-hover:text-red-600">
|
||||
Удалить
|
||||
</div>
|
||||
</div>
|
||||
|
@ -52,14 +52,50 @@ const ProfileAddressCard: React.FC<ProfileAddressCardProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between items-start self-stretch">
|
||||
<div className="flex gap-1.5 items-center cursor-pointer group" onClick={onEdit}>
|
||||
<img src="/images/edit.svg" alt="edit" width={18} height={18} className="mr-1.5 group-hover:filter-red" />
|
||||
<div className="relative text-sm leading-5 text-gray-600">Редактировать</div>
|
||||
<div className="flex justify-between items-start self-stretch max-sm:flex-row max-sm:gap-4 max-sm:justify-start max-sm:items-center">
|
||||
<div
|
||||
className="flex gap-1.5 items-center cursor-pointer group"
|
||||
onClick={onEdit}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="Редактировать адрес"
|
||||
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && onEdit && onEdit()}
|
||||
onMouseEnter={e => {
|
||||
const svg = (e.currentTarget as HTMLElement).querySelector('img');
|
||||
if (svg) (svg as HTMLImageElement).style.filter = 'invert(32%) sepia(97%) saturate(7490%) hue-rotate(355deg) brightness(97%) contrast(108%)';
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
const svg = (e.currentTarget as HTMLElement).querySelector('img');
|
||||
if (svg) (svg as HTMLImageElement).style.filter = '';
|
||||
}}
|
||||
>
|
||||
<img src="/images/edit.svg" alt="edit" width={18} height={18} className="mr-1.5" />
|
||||
<div className="relative text-sm leading-5 text-gray-600 group-hover:text-red-600 max-sm:hidden">Редактировать</div>
|
||||
</div>
|
||||
<div className="flex gap-1.5 items-center cursor-pointer group" onClick={onDelete}>
|
||||
<img src="/images/delete.svg" alt="delete" width={18} height={18} className="mr-1.5 group-hover:filter-red" />
|
||||
<div className="relative text-sm leading-5 text-gray-600">Удалить</div>
|
||||
<div
|
||||
className="flex gap-1.5 items-center cursor-pointer group"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="Удалить адрес"
|
||||
onClick={onDelete}
|
||||
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && onDelete && onDelete()}
|
||||
onMouseEnter={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#ec1c24');
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#D0D0D0');
|
||||
}}
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"
|
||||
fill="#D0D0D0"
|
||||
style={{ transition: 'fill 0.2s' }}
|
||||
/>
|
||||
</svg>
|
||||
<div className="relative text-sm leading-5 text-gray-600 group-hover:text-red-600 max-sm:hidden">Удалить</div>
|
||||
</div>
|
||||
</div>
|
||||
{onSelectMain && (
|
||||
|
@ -89,11 +89,11 @@ const ProfileBalanceCard: React.FC<ProfileBalanceCardProps> = ({
|
||||
{balance}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-5 items-end mt-5 w-full max-sm:flex-col">
|
||||
<div className="flex flex-row gap-5 items-end mt-5 w-full max-sm:flex-col max-sm:items-start">
|
||||
<div className="flex flex-col flex-1 shrink basis-0">
|
||||
<div className="flex flex-col min-w-[160px]">
|
||||
<div className="text-sm leading-snug text-gray-600">Лимит отсрочки</div>
|
||||
<div className="flex flex-col self-start mt-2">
|
||||
<div className="flex flex-col mt-2">
|
||||
<div className="text-lg font-medium leading-none text-gray-950">{limit}</div>
|
||||
<div className={`text-sm leading-snug ${isOverLimit ? 'text-red-600' : 'text-gray-600'}`}>
|
||||
{limitLeft.includes('Не установлен') ? limitLeft : `Осталось ${limitLeft}`}
|
||||
|
@ -179,7 +179,7 @@ const ProfileGarageMain = () => {
|
||||
|
||||
{!vehiclesLoading && filteredVehicles.map((vehicle) => (
|
||||
<div key={vehicle.id} className="mt-8">
|
||||
<div className="flex flex-col justify-center pr-5 py-3 w-full rounded-lg bg-slate-50 max-md:max-w-full">
|
||||
<div className="flex flex-col justify-center px-5 py-3 w-full rounded-lg bg-slate-50 max-md:max-w-full">
|
||||
<div className="flex flex-wrap gap-8 items-center w-full max-md:max-w-full">
|
||||
<div className="flex gap-8 items-center self-stretch my-auto min-w-[240px] max-md:flex-col max-md:min-w-0 max-md:gap-2">
|
||||
<div className="self-stretch my-auto text-xl font-bold leading-none text-gray-950">
|
||||
@ -203,15 +203,29 @@ const ProfileGarageMain = () => {
|
||||
<div className="flex gap-5 items-center self-stretch pr-2.5 my-auto text-sm leading-snug text-gray-600 whitespace-nowrap">
|
||||
<button
|
||||
type="button"
|
||||
className="flex gap-1.5 items-center self-stretch my-auto cursor-pointer text-sm leading-snug text-gray-600 hover:text-red-600 transition-colors"
|
||||
className="flex gap-1.5 items-center self-stretch my-auto text-sm leading-snug text-gray-600 cursor-pointer bg-transparent group"
|
||||
style={{ outline: 'none' }}
|
||||
aria-label="Удалить автомобиль"
|
||||
tabIndex={0}
|
||||
onClick={() => handleDeleteVehicle(vehicle.id)}
|
||||
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && handleDeleteVehicle(vehicle.id)}
|
||||
onMouseEnter={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#ec1c24');
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#D0D0D0');
|
||||
}}
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
src="/images/delete.svg"
|
||||
className="object-contain shrink-0 self-stretch my-auto aspect-[1.12] w-[18px]"
|
||||
/>
|
||||
<span className="self-stretch my-auto text-gray-600">
|
||||
<svg width="16" height="16" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"
|
||||
fill="#D0D0D0"
|
||||
style={{ transition: 'fill 0.2s' }}
|
||||
/>
|
||||
</svg>
|
||||
<span className="self-stretch my-auto text-gray-600 group-hover:text-red-600">
|
||||
Удалить
|
||||
</span>
|
||||
</button>
|
||||
@ -418,15 +432,29 @@ const ProfileGarageMain = () => {
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="flex gap-1.5 items-center self-stretch my-auto text-sm leading-snug text-gray-600 cursor-pointer bg-transparent hover:text-red-600 transition-colors"
|
||||
className="flex gap-1.5 items-center self-stretch my-auto text-sm leading-snug text-gray-600 cursor-pointer bg-transparent group"
|
||||
style={{ outline: 'none' }}
|
||||
aria-label="Удалить из истории поиска"
|
||||
tabIndex={0}
|
||||
onClick={() => handleDeleteFromHistory(historyItem.id)}
|
||||
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && handleDeleteFromHistory(historyItem.id)}
|
||||
onMouseEnter={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#ec1c24');
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#D0D0D0');
|
||||
}}
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
src="/images/delete.svg"
|
||||
className="object-contain shrink-0 self-stretch my-auto aspect-[1.12] w-[18px]"
|
||||
/>
|
||||
<span className="self-stretch my-auto text-gray-600">
|
||||
<svg width="16" height="16" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"
|
||||
fill="#D0D0D0"
|
||||
style={{ transition: 'fill 0.2s' }}
|
||||
/>
|
||||
</svg>
|
||||
<span className="self-stretch my-auto text-gray-600 group-hover:text-red-600">
|
||||
Удалить
|
||||
</span>
|
||||
</button>
|
||||
|
@ -80,24 +80,25 @@ const ProfileHistoryItem: React.FC<ProfileHistoryItemProps> = ({
|
||||
<div className="w-16 text-center max-md:w-full">
|
||||
<button
|
||||
onClick={handleDeleteClick}
|
||||
className="p-2 text-red-500 hover:text-red-700 hover:bg-red-50 rounded-lg transition-colors group"
|
||||
className="flex items-center p-2 group"
|
||||
title="Удалить из истории"
|
||||
aria-label="Удалить из истории"
|
||||
tabIndex={0}
|
||||
onMouseEnter={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#ec1c24');
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
const path = e.currentTarget.querySelector('path');
|
||||
if (path) path.setAttribute('fill', '#D0D0D0');
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="transition-colors"
|
||||
>
|
||||
<path d="M3 6h18" className="group-hover:stroke-[#ec1c24]" />
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" className="group-hover:stroke-[#ec1c24]" />
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c-1 0 2 1 2 2v2" className="group-hover:stroke-[#ec1c24]" />
|
||||
<svg width="16" height="16" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"
|
||||
fill="#D0D0D0"
|
||||
style={{ transition: 'fill 0.2s' }}
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -272,7 +272,7 @@ const ProfileHistoryMain = () => {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-[526px]">
|
||||
<div className="flex flex-wrap gap-5 items-center px-8 py-3 w-full leading-snug text-gray-400 whitespace-nowrap bg-white rounded-lg max-md:px-5 max-md:max-w-full max-md:flex-col">
|
||||
<div className="flex gap-5 items-center px-8 py-3 w-full leading-snug text-gray-400 whitespace-nowrap bg-white rounded-lg max-md:px-5 max-md:max-w-full">
|
||||
<div className="flex-1 shrink self-stretch my-auto text-gray-400 basis-0 text-ellipsis max-md:max-w-full max-md:w-full">
|
||||
<SearchInput
|
||||
value={search}
|
||||
@ -280,7 +280,7 @@ const ProfileHistoryMain = () => {
|
||||
placeholder="Поиск в истории..."
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex gap-2 max-sm:hidden">
|
||||
{(selectedManufacturer !== "Все" || search.trim() || activeTab !== "Все") && (
|
||||
<button
|
||||
onClick={() => {
|
||||
|
@ -89,7 +89,7 @@ const ProfileHistoryTabs: React.FC<ProfileHistoryTabsProps> = ({
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
className="relative w-[240px] max-w-full"
|
||||
className="relative w-[240px] max-w-full max-sm:w-full"
|
||||
ref={dropdownRef}
|
||||
tabIndex={0}
|
||||
>
|
||||
|
@ -10,7 +10,7 @@ const ProfileSettingsActionsBlock: React.FC<ProfileSettingsActionsBlockProps> =
|
||||
Сохранить изменения
|
||||
</div>
|
||||
<div className="gap-2.5 self-stretch px-5 py-4 my-auto rounded-xl border border-red-600 min-h-[50px] min-w-[240px] cursor-pointer bg-white text-gray-950" onClick={onAddLegalEntity}>
|
||||
Добавить юридическое лицо
|
||||
Добавить юр лицо
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,27 @@
|
||||
import React from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { useRouter } from 'next/router';
|
||||
import { GET_LAXIMO_UNIT_INFO, GET_LAXIMO_UNIT_IMAGE_MAP } from '@/lib/graphql';
|
||||
import BrandSelectionModal from '../BrandSelectionModal';
|
||||
|
||||
interface KnotInProps {
|
||||
catalogCode: string;
|
||||
vehicleId: string;
|
||||
ssd?: string;
|
||||
unitId: string;
|
||||
unitName?: string;
|
||||
parts?: Array<{
|
||||
detailid?: string;
|
||||
codeonimage?: string | number;
|
||||
oem?: string;
|
||||
name?: string;
|
||||
price?: string | number;
|
||||
brand?: string;
|
||||
availability?: string;
|
||||
note?: string;
|
||||
attributes?: Array<{ key: string; name?: string; value: string }>;
|
||||
}>;
|
||||
}
|
||||
|
||||
// Функция для корректного формирования URL изображения
|
||||
const getImageUrl = (baseUrl: string, size: string) => {
|
||||
@ -11,25 +34,138 @@ const getImageUrl = (baseUrl: string, size: string) => {
|
||||
.replace('%size%', size);
|
||||
};
|
||||
|
||||
const KnotIn = ({ node }: { node: any }) => {
|
||||
if (!node) return null;
|
||||
let imageUrl = '';
|
||||
if (node.imageurl) {
|
||||
imageUrl = getImageUrl(node.imageurl, '250');
|
||||
} else if (node.largeimageurl) {
|
||||
imageUrl = node.largeimageurl;
|
||||
const KnotIn: React.FC<KnotInProps> = ({ catalogCode, vehicleId, ssd, unitId, unitName, parts }) => {
|
||||
const imgRef = useRef<HTMLImageElement>(null);
|
||||
const [imageScale, setImageScale] = useState({ x: 1, y: 1 });
|
||||
const selectedImageSize = 'source';
|
||||
const [isBrandModalOpen, setIsBrandModalOpen] = useState(false);
|
||||
const [selectedDetail, setSelectedDetail] = useState<{ oem: string; name: string } | null>(null);
|
||||
const router = useRouter();
|
||||
|
||||
// Получаем инфо об узле (для картинки)
|
||||
const { data: unitInfoData, loading: unitInfoLoading, error: unitInfoError } = useQuery(
|
||||
GET_LAXIMO_UNIT_INFO,
|
||||
{
|
||||
variables: { catalogCode, vehicleId, unitId, ssd: ssd || '' },
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || !unitId,
|
||||
errorPolicy: 'all',
|
||||
}
|
||||
);
|
||||
// Получаем карту координат
|
||||
const { data: imageMapData, loading: imageMapLoading, error: imageMapError } = useQuery(
|
||||
GET_LAXIMO_UNIT_IMAGE_MAP,
|
||||
{
|
||||
variables: { catalogCode, vehicleId, unitId, ssd: ssd || '' },
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || !unitId,
|
||||
errorPolicy: 'all',
|
||||
}
|
||||
);
|
||||
|
||||
const unitInfo = unitInfoData?.laximoUnitInfo;
|
||||
const coordinates = imageMapData?.laximoUnitImageMap?.coordinates || [];
|
||||
const imageUrl = unitInfo?.imageurl ? getImageUrl(unitInfo.imageurl, selectedImageSize) : '';
|
||||
|
||||
// Масштабируем точки после загрузки картинки
|
||||
const handleImageLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {
|
||||
const img = e.currentTarget;
|
||||
if (!img.naturalWidth || !img.naturalHeight) return;
|
||||
setImageScale({
|
||||
x: img.offsetWidth / img.naturalWidth,
|
||||
y: img.offsetHeight / img.naturalHeight,
|
||||
});
|
||||
};
|
||||
|
||||
// Клик по точке: найти part по codeonimage/detailid и открыть BrandSelectionModal
|
||||
const handlePointClick = (codeonimage: string | number) => {
|
||||
if (!parts) return;
|
||||
console.log('Клик по точке:', codeonimage, 'Все детали:', parts);
|
||||
const part = parts.find(
|
||||
(p) =>
|
||||
(p.codeonimage && p.codeonimage.toString() === codeonimage.toString()) ||
|
||||
(p.detailid && p.detailid.toString() === codeonimage.toString())
|
||||
);
|
||||
console.log('Найдена деталь для точки:', part);
|
||||
if (part?.oem) {
|
||||
setSelectedDetail({ oem: part.oem, name: part.name || '' });
|
||||
setIsBrandModalOpen(true);
|
||||
} else {
|
||||
console.warn('Нет артикула (oem) для выбранной точки:', codeonimage, part);
|
||||
}
|
||||
};
|
||||
|
||||
// Для отладки: вывести детали и координаты
|
||||
React.useEffect(() => {
|
||||
console.log('KnotIn parts:', parts);
|
||||
console.log('KnotIn coordinates:', coordinates);
|
||||
}, [parts, coordinates]);
|
||||
|
||||
if (unitInfoLoading || imageMapLoading) {
|
||||
return <div className="text-center py-8 text-gray-500">Загружаем схему узла...</div>;
|
||||
}
|
||||
if (unitInfoError) {
|
||||
return <div className="text-center py-8 text-red-600">Ошибка загрузки схемы: {unitInfoError.message}</div>;
|
||||
}
|
||||
if (!imageUrl) {
|
||||
return <div className="text-center py-8 text-gray-400">Нет изображения для этого узла</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="knotin">
|
||||
{imageUrl ? (
|
||||
<img src={imageUrl} loading="lazy" alt={node.name || "Изображение узла"} className="image-26" />
|
||||
) : (
|
||||
<div style={{ width: 200, height: 200, background: '#eee', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
Нет изображения
|
||||
<>
|
||||
<div className="relative inline-block">
|
||||
{/* ВРЕМЕННО: выводим количество точек для быстрой проверки */}
|
||||
<div style={{ position: 'absolute', top: 4, left: 4, zIndex: 20, background: 'rgba(255,0,0,0.1)', color: '#c00', fontWeight: 700, fontSize: 14, padding: '2px 8px', borderRadius: 6 }}>
|
||||
{coordinates.length} точек
|
||||
</div>
|
||||
)}
|
||||
{/* <div style={{ marginTop: 8, fontWeight: 500 }}>{node.name}</div> */}
|
||||
<img
|
||||
ref={imgRef}
|
||||
src={imageUrl}
|
||||
loading="lazy"
|
||||
alt={unitName || unitInfo?.name || "Изображение узла"}
|
||||
onLoad={handleImageLoad}
|
||||
className="max-w-full h-auto mx-auto rounded"
|
||||
style={{ maxWidth: 400, display: 'block' }}
|
||||
/>
|
||||
{/* Точки/области */}
|
||||
{coordinates.map((coord: any, idx: number) => {
|
||||
const scaledX = coord.x * imageScale.x;
|
||||
const scaledY = coord.y * imageScale.y;
|
||||
const scaledWidth = coord.width * imageScale.x;
|
||||
const scaledHeight = coord.height * imageScale.y;
|
||||
return (
|
||||
<div
|
||||
key={`coord-${unitId}-${idx}-${coord.x}-${coord.y}`}
|
||||
tabIndex={0}
|
||||
aria-label={`Деталь ${coord.codeonimage}`}
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Enter' || e.key === ' ') handlePointClick(coord.codeonimage);
|
||||
}}
|
||||
className="absolute flex items-center justify-center border-2 border-red-600 bg-white rounded-full cursor-pointer"
|
||||
style={{
|
||||
left: scaledX,
|
||||
top: scaledY,
|
||||
width: scaledWidth,
|
||||
height: scaledHeight,
|
||||
borderRadius: '50%',
|
||||
pointerEvents: 'auto',
|
||||
}}
|
||||
title={coord.codeonimage}
|
||||
onClick={() => handlePointClick(coord.codeonimage)}
|
||||
>
|
||||
<span className="flex items-center justify-center w-full h-full text-black text-sm font-bold select-none pointer-events-none">
|
||||
{coord.codeonimage}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/* Модалка выбора бренда */}
|
||||
<BrandSelectionModal
|
||||
isOpen={isBrandModalOpen}
|
||||
onClose={() => setIsBrandModalOpen(false)}
|
||||
articleNumber={selectedDetail?.oem || ''}
|
||||
detailName={selectedDetail?.name || ''}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import BrandSelectionModal from '../BrandSelectionModal';
|
||||
|
||||
interface KnotPartsProps {
|
||||
parts: Array<{
|
||||
@ -13,39 +14,59 @@ interface KnotPartsProps {
|
||||
note?: string;
|
||||
attributes?: Array<{ key: string; name?: string; value: string }>;
|
||||
}>;
|
||||
selectedCodeOnImage?: string | number;
|
||||
}
|
||||
|
||||
const KnotParts: React.FC<KnotPartsProps> = ({ parts }) => {
|
||||
const router = useRouter();
|
||||
const KnotParts: React.FC<KnotPartsProps> = ({ parts, selectedCodeOnImage }) => {
|
||||
const [isBrandModalOpen, setIsBrandModalOpen] = useState(false);
|
||||
const [selectedDetail, setSelectedDetail] = useState<{ oem: string; name: string } | null>(null);
|
||||
|
||||
const handlePriceClick = (part: any) => {
|
||||
if (part.oem) {
|
||||
setSelectedDetail({ oem: part.oem, name: part.name || '' });
|
||||
setIsBrandModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="knot-parts">
|
||||
{parts.map((part, idx) => (
|
||||
<div className="w-layout-hflex knotlistitem" key={part.detailid || idx}>
|
||||
<div className="w-layout-hflex flex-block-116">
|
||||
<div className="nuberlist">{part.codeonimage || idx + 1}</div>
|
||||
<div className="oemnuber">{part.oem}</div>
|
||||
</div>
|
||||
<div className="partsname">{part.name}</div>
|
||||
<div className="w-layout-hflex flex-block-117">
|
||||
<button
|
||||
className="button-3 w-button"
|
||||
onClick={() => {
|
||||
if (part.oem) {
|
||||
router.push(`/search?q=${encodeURIComponent(part.oem)}&mode=parts`);
|
||||
}
|
||||
}}
|
||||
<>
|
||||
<div className="knot-parts">
|
||||
{parts.map((part, idx) => {
|
||||
const isSelected = part.codeonimage && part.codeonimage === selectedCodeOnImage;
|
||||
return (
|
||||
<div
|
||||
className={`w-layout-hflex knotlistitem border rounded transition-colors duration-150 ${isSelected ? 'bg-yellow-100 border-yellow-400' : 'border-transparent'}`}
|
||||
key={part.detailid || idx}
|
||||
>
|
||||
Цена
|
||||
</button>
|
||||
<div className="code-embed-16 w-embed">
|
||||
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.1 13.5H9.89999V8.1H8.1V13.5ZM8.99999 6.3C9.25499 6.3 9.46889 6.2136 9.64169 6.0408C9.81449 5.868 9.90059 5.6544 9.89999 5.4C9.89939 5.1456 9.81299 4.932 9.64079 4.7592C9.46859 4.5864 9.25499 4.5 8.99999 4.5C8.745 4.5 8.53139 4.5864 8.35919 4.7592C8.187 4.932 8.1006 5.1456 8.1 5.4C8.0994 5.6544 8.1858 5.8683 8.35919 6.0417C8.53259 6.2151 8.74619 6.3012 8.99999 6.3ZM8.99999 18C7.755 18 6.585 17.7636 5.49 17.2908C4.395 16.818 3.4425 16.1769 2.6325 15.3675C1.8225 14.5581 1.1814 13.6056 0.709201 12.51C0.237001 11.4144 0.000601139 10.2444 1.13924e-06 9C-0.00059886 7.7556 0.235801 6.5856 0.709201 5.49C1.1826 4.3944 1.8237 3.4419 2.6325 2.6325C3.4413 1.8231 4.3938 1.182 5.49 0.7092C6.5862 0.2364 7.7562 0 8.99999 0C10.2438 0 11.4138 0.2364 12.51 0.7092C13.6062 1.182 14.5587 1.8231 15.3675 2.6325C16.1763 3.4419 16.8177 4.3944 17.2917 5.49C17.7657 6.5856 18.0018 7.7556 18 9C17.9982 10.2444 17.7618 11.4144 17.2908 12.51C16.8198 13.6056 16.1787 14.5581 15.3675 15.3675C14.5563 16.1769 13.6038 16.8183 12.51 17.2917C11.4162 17.7651 10.2462 18.0012 8.99999 18Z" fill="currentcolor" />
|
||||
</svg>
|
||||
<div className="w-layout-hflex flex-block-116">
|
||||
<div className="nuberlist">{part.codeonimage || idx + 1}</div>
|
||||
<div className="oemnuber">{part.oem}</div>
|
||||
</div>
|
||||
<div className="partsname">{part.name}</div>
|
||||
<div className="w-layout-hflex flex-block-117">
|
||||
<button
|
||||
className="button-3 w-button"
|
||||
onClick={() => handlePriceClick(part)}
|
||||
>
|
||||
Цена
|
||||
</button>
|
||||
<div className="code-embed-16 w-embed">
|
||||
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.1 13.5H9.89999V8.1H8.1V13.5ZM8.99999 6.3C9.25499 6.3 9.46889 6.2136 9.64169 6.0408C9.81449 5.868 9.90059 5.6544 9.89999 5.4C9.89939 5.1456 9.81299 4.932 9.64079 4.7592C9.46859 4.5864 9.25499 4.5 8.99999 4.5C8.745 4.5 8.53139 4.5864 8.35919 4.7592C8.187 4.932 8.1006 5.1456 8.1 5.4C8.0994 5.6544 8.1858 5.8683 8.35919 6.0417C8.53259 6.2151 8.74619 6.3012 8.99999 6.3ZM8.99999 18C7.755 18 6.585 17.7636 5.49 17.2908C4.395 16.818 3.4425 16.1769 2.6325 15.3675C1.8225 14.5581 1.1814 13.6056 0.709201 12.51C0.237001 11.4144 0.000601139 10.2444 1.13924e-06 9C-0.00059886 7.7556 0.235801 6.5856 0.709201 5.49C1.1826 4.3944 1.8237 3.4419 2.6325 2.6325C3.4413 1.8231 4.3938 1.182 5.49 0.7092C6.5862 0.2364 7.7562 0 8.99999 0C10.2438 0 11.4138 0.2364 12.51 0.7092C13.6062 1.182 14.5587 1.8231 15.3675 2.6325C16.1763 3.4419 16.8177 4.3944 17.2917 5.49C17.7657 6.5856 18.0018 7.7556 18 9C17.9982 10.2444 17.7618 11.4144 17.2908 12.51C16.8198 13.6056 16.1787 14.5581 15.3675 15.3675C14.5563 16.1769 13.6038 16.8183 12.51 17.2917C11.4162 17.7651 10.2462 18.0012 8.99999 18Z" fill="currentcolor" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<BrandSelectionModal
|
||||
isOpen={isBrandModalOpen}
|
||||
onClose={() => setIsBrandModalOpen(false)}
|
||||
articleNumber={selectedDetail?.oem || ''}
|
||||
detailName={selectedDetail?.name || ''}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,7 @@ interface VinCategoryProps {
|
||||
const VinCategory: React.FC<VinCategoryProps> = ({ catalogCode, vehicleId, ssd, onNodeSelect }) => {
|
||||
const { data: categoriesData, loading: categoriesLoading, error: categoriesError } = useQuery(GET_LAXIMO_CATEGORIES, {
|
||||
variables: { catalogCode, vehicleId, ssd },
|
||||
skip: !catalogCode || !vehicleId,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null,
|
||||
errorPolicy: "all",
|
||||
});
|
||||
const categories = categoriesData?.laximoCategories || [];
|
||||
|
@ -25,7 +25,7 @@ const VinLeftbar: React.FC<VinLeftbarProps> = ({ vehicleInfo, onSearchResults, o
|
||||
|
||||
const { data: categoriesData, loading: categoriesLoading, error: categoriesError } = useQuery(GET_LAXIMO_CATEGORIES, {
|
||||
variables: { catalogCode, vehicleId, ssd },
|
||||
skip: !catalogCode || !vehicleId,
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null,
|
||||
errorPolicy: 'all'
|
||||
});
|
||||
const categories = categoriesData?.laximoCategories || [];
|
||||
@ -88,7 +88,7 @@ const VinLeftbar: React.FC<VinLeftbarProps> = ({ vehicleInfo, onSearchResults, o
|
||||
}, [searchResults, searchQuery, onSearchResults]);
|
||||
|
||||
// --- Новый блок: вычисляем доступность поиска ---
|
||||
const isSearchAvailable = !!catalogCode && !!vehicleId && !!ssd && ssd.trim() !== '';
|
||||
const isSearchAvailable = !!catalogCode && vehicleId !== undefined && vehicleId !== null && !!ssd && ssd.trim() !== '';
|
||||
const showWarning = !isSearchAvailable;
|
||||
const showError = !!error && isSearchAvailable && searchQuery.trim();
|
||||
const showNotFound = isSearchAvailable && searchQuery.trim() && !loading && data && searchResults && searchResults.details && searchResults.details.length === 0;
|
||||
@ -98,7 +98,7 @@ const VinLeftbar: React.FC<VinLeftbarProps> = ({ vehicleInfo, onSearchResults, o
|
||||
const [selectedQuickGroup, setSelectedQuickGroup] = useState<any | null>(null);
|
||||
const { data: quickGroupsData, loading: quickGroupsLoading, error: quickGroupsError } = useQuery(GET_LAXIMO_QUICK_GROUPS, {
|
||||
variables: { catalogCode, vehicleId, ssd },
|
||||
skip: !catalogCode || !vehicleId || activeTab !== 'manufacturer',
|
||||
skip: !catalogCode || vehicleId === undefined || vehicleId === null || activeTab !== 'manufacturer',
|
||||
errorPolicy: 'all'
|
||||
});
|
||||
const quickGroups = quickGroupsData?.laximoQuickGroups || [];
|
||||
@ -136,12 +136,30 @@ const VinLeftbar: React.FC<VinLeftbarProps> = ({ vehicleInfo, onSearchResults, o
|
||||
const skipQuickDetail =
|
||||
!selectedQuickGroup ||
|
||||
!catalogCode ||
|
||||
!vehicleId ||
|
||||
!selectedQuickGroup.quickgroupid ||
|
||||
!ssd;
|
||||
vehicleId === undefined ||
|
||||
vehicleId === null ||
|
||||
!selectedQuickGroup?.quickgroupid ||
|
||||
!ssd ||
|
||||
ssd.trim() === '';
|
||||
|
||||
console.log('QuickDetail QUERY VARS', {
|
||||
catalogCode,
|
||||
vehicleId,
|
||||
quickGroupId: selectedQuickGroup?.quickgroupid,
|
||||
ssd: ssd ? `${ssd.substring(0, 30)}...` : 'отсутствует'
|
||||
});
|
||||
|
||||
console.log('QuickDetail SKIP CONDITIONS', {
|
||||
hasSelectedQuickGroup: !!selectedQuickGroup,
|
||||
hasCatalogCode: !!catalogCode,
|
||||
hasVehicleId: vehicleId !== undefined && vehicleId !== null,
|
||||
hasQuickGroupId: !!selectedQuickGroup?.quickgroupid,
|
||||
hasSsd: !!ssd && ssd.trim() !== '',
|
||||
skipQuickDetail
|
||||
});
|
||||
|
||||
const { data: quickDetailData, loading: quickDetailLoading, error: quickDetailError } = useQuery(GET_LAXIMO_QUICK_DETAIL, {
|
||||
variables: selectedQuickGroup ? {
|
||||
variables: selectedQuickGroup?.quickgroupid && !skipQuickDetail ? {
|
||||
catalogCode,
|
||||
vehicleId,
|
||||
quickGroupId: selectedQuickGroup.quickgroupid,
|
||||
|
@ -58,7 +58,7 @@ const ProfileActsPage = () => {
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="page-wrapper">
|
||||
<>
|
||||
<Head>
|
||||
<title>ProfileActs</title>
|
||||
<meta content="ProfileActs" property="og:title" />
|
||||
@ -78,7 +78,7 @@ const ProfileActsPage = () => {
|
||||
</section>
|
||||
<MobileMenuBottomSection />
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -123,7 +123,7 @@ const VehicleDetailsPage = () => {
|
||||
...(finalSsd && { ssd: finalSsd }),
|
||||
localized: true
|
||||
},
|
||||
skip: !brand || !vehicleId,
|
||||
skip: !brand || vehicleId === undefined || vehicleId === null,
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
);
|
||||
@ -194,8 +194,9 @@ const VehicleDetailsPage = () => {
|
||||
);
|
||||
}
|
||||
|
||||
// Если vehicleId невалидный (например, '0'), показываем предупреждение и не рендерим поиск
|
||||
if (!vehicleId || vehicleId === '0') {
|
||||
// Если 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">
|
||||
@ -214,7 +215,8 @@ const VehicleDetailsPage = () => {
|
||||
|
||||
// Гарантируем, что vehicleId — строка
|
||||
const vehicleIdStr = Array.isArray(vehicleId) ? (vehicleId[0] || '') : (vehicleId || '');
|
||||
const fallbackVehicleId = (vehicleIdStr !== '0' ? vehicleIdStr : '');
|
||||
// Для Laximo API vehicleId может быть '0' для автомобилей, найденных по VIN
|
||||
const fallbackVehicleId = vehicleIdStr;
|
||||
|
||||
let vehicleInfo = vehicleData?.laximoVehicleInfo || {
|
||||
vehicleid: fallbackVehicleId,
|
||||
@ -225,8 +227,8 @@ const VehicleDetailsPage = () => {
|
||||
attributes: [] as never[]
|
||||
};
|
||||
|
||||
// Если вдруг с сервера пришёл vehicleid: '0', подменяем на корректный
|
||||
if (vehicleInfo.vehicleid === '0' && fallbackVehicleId) {
|
||||
// Убеждаемся, что vehicleid соответствует параметру из URL
|
||||
if (vehicleInfo.vehicleid !== fallbackVehicleId && fallbackVehicleId) {
|
||||
vehicleInfo = { ...vehicleInfo, vehicleid: fallbackVehicleId };
|
||||
}
|
||||
|
||||
@ -303,7 +305,14 @@ const VehicleDetailsPage = () => {
|
||||
<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>
|
||||
<KnotIn node={selectedNode} />
|
||||
<KnotIn
|
||||
catalogCode={vehicleInfo.catalog}
|
||||
vehicleId={vehicleInfo.vehicleid}
|
||||
ssd={vehicleInfo.ssd}
|
||||
unitId={selectedNode.unitid}
|
||||
unitName={selectedNode.name}
|
||||
parts={unitDetails}
|
||||
/>
|
||||
{unitDetailsLoading ? (
|
||||
<div style={{ padding: 24, textAlign: 'center' }}>Загружаем детали узла...</div>
|
||||
) : unitDetailsError ? (
|
||||
|
@ -74,12 +74,12 @@ const PartDetailPage = () => {
|
||||
oemNumber: oemNumber,
|
||||
ssd: finalSsd
|
||||
},
|
||||
skip: !brand || !vehicleId || !oemNumber || !finalSsd,
|
||||
skip: !brand || vehicleId === undefined || vehicleId === null || !oemNumber || !finalSsd,
|
||||
errorPolicy: 'all'
|
||||
}
|
||||
);
|
||||
|
||||
if (!brand || !vehicleId || !oemNumber) {
|
||||
if (!brand || vehicleId === undefined || vehicleId === null || !oemNumber) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
|
@ -386,15 +386,17 @@ input.input-receiver:focus {
|
||||
color: var(--_fonts---color--light-blue-grey);
|
||||
}
|
||||
|
||||
.knotin {
|
||||
/* .knotin {
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
.knotin img {
|
||||
max-width: 100%;
|
||||
object-fit: contain; /* или cover */
|
||||
}
|
||||
object-fit: contain;
|
||||
} */
|
||||
|
||||
|
||||
|
||||
.tabs-menu.w-tab-menu {
|
||||
scrollbar-width: none;
|
||||
@ -428,4 +430,32 @@ input#VinSearchInput {
|
||||
text-overflow: ellipsis;
|
||||
max-height: 2.8em;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.w-layout-hflex.flex-block-6 {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.div-block-12,
|
||||
.div-block-12.small,
|
||||
.div-block-12-copy,
|
||||
.div-block-12-copy.small,
|
||||
.div-block-123,
|
||||
.div-block-123.small,
|
||||
.div-block-red,
|
||||
.div-block-red.small {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.flex-block-6 {
|
||||
grid-template-columns: 1fr !important;
|
||||
grid-template-rows: none !important;
|
||||
grid-template-areas: none !important;
|
||||
grid-auto-flow: row !important; /* <--- ВАЖНО! */
|
||||
}
|
||||
}
|
@ -787,6 +787,8 @@
|
||||
.w-layout-blockcontainer {
|
||||
max-width: 728px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
@ -794,6 +796,8 @@
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.w-commerce-commercelayoutcontainer {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
@ -4024,6 +4028,7 @@ body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.button-for-mobile-menu-block:hover {
|
||||
background-color: var(--_button---hover-dark_blue);
|
||||
}
|
||||
@ -5426,10 +5431,10 @@ body {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.topmenuh {
|
||||
/*
|
||||
.bottom_head {
|
||||
margin-top: 0;
|
||||
}
|
||||
} */
|
||||
|
||||
.flex-block-4 {
|
||||
grid-column-gap: 40px;
|
||||
@ -5579,6 +5584,8 @@ body {
|
||||
background-color: var(--white);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.button-for-mobile-menu-block {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
@ -5680,9 +5687,9 @@ body {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.topmenub {
|
||||
/* .bottom_head {
|
||||
margin-top: 0;
|
||||
}
|
||||
} */
|
||||
|
||||
.vinleftbar {
|
||||
width: 320px;
|
||||
@ -6082,7 +6089,7 @@ body {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.top_head, .topmenuh {
|
||||
.top_head, .bottom_head {
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
@ -6882,7 +6889,7 @@ body {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.topmenub {
|
||||
.bottom_head {
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
@ -7543,8 +7550,9 @@ body {
|
||||
}
|
||||
|
||||
.flex-block-18-copy-copy {
|
||||
grid-column-gap: 10px;
|
||||
grid-row-gap: 10px;
|
||||
grid-column-gap: 40px;
|
||||
grid-row-gap: 40px;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.link-block-4-copy {
|
||||
@ -7602,8 +7610,9 @@ body {
|
||||
}
|
||||
|
||||
.flex-block-87 {
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
grid-column-gap: 10px;
|
||||
grid-row-gap: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mobile-menu-bottom {
|
||||
@ -7627,10 +7636,16 @@ body {
|
||||
}
|
||||
|
||||
.button-for-mobile-menu-block {
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
width: 60px;
|
||||
padding-bottom: 5px;
|
||||
grid-column-gap: 2px;
|
||||
grid-row-gap: 2px;
|
||||
background-color: var(--_fonts---color--white);
|
||||
color: var(--_button---light-blue-grey);
|
||||
flex-flow: column;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.button-for-mobile-menu-block:hover {
|
||||
background-color: var(--_button---light-blue);
|
||||
}
|
||||
|
||||
.section-3 {
|
||||
@ -7643,7 +7658,8 @@ body {
|
||||
}
|
||||
|
||||
.flex-block-93 {
|
||||
margin-left: 0;
|
||||
align-self: auto;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.sort-list-card {
|
||||
@ -7882,6 +7898,10 @@ body {
|
||||
.container-copy.footer {
|
||||
padding-bottom: 90px;
|
||||
}
|
||||
|
||||
.mobile-menu-buttom-section {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 479px) {
|
||||
@ -7951,7 +7971,7 @@ body {
|
||||
grid-row-gap: 15px;
|
||||
}
|
||||
|
||||
.top_head, .topmenuh {
|
||||
.top_head, .bottom_head {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
@ -9132,7 +9152,7 @@ body {
|
||||
top: 58px;
|
||||
}
|
||||
|
||||
.topmenub {
|
||||
.bottom_head {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
@ -9400,6 +9420,12 @@ body {
|
||||
#w-node-_35f55517-cbe0-9ee3-13bb-a3ed00029bba-00029ba8, #w-node-_35f55517-cbe0-9ee3-13bb-a3ed00029bc7-00029ba8 {
|
||||
justify-self: stretch;
|
||||
}
|
||||
.button-for-mobile-menu-block {
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
width: 60px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-block-113 {
|
||||
|
Reference in New Issue
Block a user