Добавлено получение информации о деталях из Parts Index и обновлены компоненты для отображения этой информации. Включены новые типы для работы с данными Parts Index.

This commit is contained in:
Bivekich
2025-06-27 15:31:48 +03:00
parent d44874775c
commit 855018bd6c
6 changed files with 258 additions and 4 deletions

View File

@ -30,6 +30,7 @@ interface CoreProductCardProps {
isAnalog?: boolean;
isLoadingOffers?: boolean;
onLoadOffers?: () => void;
partsIndexPowered?: boolean;
}
const CoreProductCard: React.FC<CoreProductCardProps> = ({
@ -41,7 +42,8 @@ const CoreProductCard: React.FC<CoreProductCardProps> = ({
showMoreText,
isAnalog = false,
isLoadingOffers = false,
onLoadOffers
onLoadOffers,
partsIndexPowered = false
}) => {
const { addItem } = useCart();
const { addToFavorites, removeFromFavorites, isFavorite } = useFavorites();
@ -196,6 +198,11 @@ const CoreProductCard: React.FC<CoreProductCardProps> = ({
{image && (
<div className="div-block-20">
<img src={image} loading="lazy" alt={name} className="image-10" />
{partsIndexPowered && (
<div className="text-xs text-gray-500 mt-1 text-center">
powered by <span className="font-semibold text-blue-600">Parts Index</span>
</div>
)}
</div>
)}
</div>
@ -246,6 +253,11 @@ const CoreProductCard: React.FC<CoreProductCardProps> = ({
{image && (
<div className="div-block-20">
<img src={image} loading="lazy" alt={name} className="image-10" />
{partsIndexPowered && (
<div className="text-xs text-gray-500 mt-1 text-center">
powered by <span className="font-semibold text-blue-600">Parts Index</span>
</div>
)}
</div>
)}
</div>

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { partsIndexService } from '@/lib/partsindex-service';
import { PartsIndexCatalog, PartsIndexGroup, PartsIndexTabData } from '@/types/partsindex';
import { PartsIndexCatalog, PartsIndexGroup, PartsIndexTabData, PartsIndexEntityInfo } from '@/types/partsindex';
export const usePartsIndexCatalogs = () => {
const [catalogs, setCatalogs] = useState<PartsIndexCatalog[]>([]);
@ -59,6 +59,44 @@ export const usePartsIndexCatalogGroups = (catalogId: string | null) => {
return { group, loading, error };
};
export const usePartsIndexEntityInfo = (code: string | null, brand?: string | null) => {
const [entityInfo, setEntityInfo] = useState<PartsIndexEntityInfo | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!code) {
setEntityInfo(null);
return;
}
const fetchEntityInfo = async () => {
try {
setLoading(true);
setError(null);
const response = await partsIndexService.getEntityInfo(code, brand || undefined, 'ru');
// Берем первый элемент из списка, если он есть
if (response.list && response.list.length > 0) {
setEntityInfo(response.list[0]);
} else {
setEntityInfo(null);
}
} catch (err) {
setError(err as Error);
console.error(`Ошибка загрузки информации о детали ${code}:`, err);
setEntityInfo(null);
} finally {
setLoading(false);
}
};
fetchEntityInfo();
}, [code, brand]);
return { entityInfo, loading, error };
};
// Функция для преобразования данных Parts Index в формат меню
export const transformPartsIndexToTabData = (
catalogs: PartsIndexCatalog[],

View File

@ -1,4 +1,4 @@
import { PartsIndexCatalogsResponse, PartsIndexGroup } from '@/types/partsindex';
import { PartsIndexCatalogsResponse, PartsIndexGroup, PartsIndexEntityInfoResponse } from '@/types/partsindex';
const PARTS_INDEX_API_BASE = 'https://api.parts-index.com';
const API_KEY = 'PI-E1C0ADB7-E4A8-4960-94A0-4D9C0A074DAE';
@ -54,6 +54,41 @@ class PartsIndexService {
throw error;
}
}
/**
* Получить информацию о детали по артикулу и бренду
*/
async getEntityInfo(code: string, brand?: string, lang: string = 'ru'): Promise<PartsIndexEntityInfoResponse> {
try {
const params = new URLSearchParams({
code: code,
lang: lang
});
if (brand) {
params.append('brand', brand);
}
const response = await fetch(
`${PARTS_INDEX_API_BASE}/v1/entities?${params.toString()}`,
{
headers: {
'Accept': 'application/json',
'Authorization': API_KEY,
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error(`Ошибка получения информации о детали ${code}:`, error);
throw error;
}
}
}
export const partsIndexService = new PartsIndexService();

View File

@ -15,6 +15,7 @@ import CatalogSortDropdown from "@/components/CatalogSortDropdown";
import MobileMenuBottomSection from '../components/MobileMenuBottomSection';
import { SEARCH_PRODUCT_OFFERS, GET_ANALOG_OFFERS } from "@/lib/graphql";
import { useArticleImage } from "@/hooks/useArticleImage";
import { usePartsIndexEntityInfo } from "@/hooks/usePartsIndex";
const ANALOGS_CHUNK_SIZE = 5;
@ -255,6 +256,12 @@ export default function SearchResult() {
});
const { imageUrl: mainImageUrl } = useArticleImage(artId as string, { enabled: !!artId });
// Получаем информацию о детали из Parts Index
const { entityInfo, loading: partsIndexLoading } = usePartsIndexEntityInfo(
searchQuery || null,
brandQuery || null
);
const [
getAnalogOffers,
@ -568,15 +575,20 @@ export default function SearchResult() {
return null;
}
// Используем фотографию из Parts Index, если она есть, иначе fallback на mainImageUrl
const partsIndexImage = entityInfo?.images?.[0];
const displayImage = partsIndexImage || mainImageUrl;
return (
<>
<CoreProductCard
brand={result.brand}
article={result.articleNumber}
name={result.name}
image={mainImageUrl}
image={displayImage}
offers={mainProductOffers}
showMoreText={mainProductOffers.length < filteredOffers.filter(o => !o.isAnalog).length ? "Показать еще" : undefined}
partsIndexPowered={!!partsIndexImage}
/>
</>
);

View File

@ -152,4 +152,44 @@ export interface PartsIndexParamsVariables {
generationId?: string;
params?: string;
q?: string;
}
// Типы для получения информации о детали по артикулу
export interface PartsIndexEntityInfo {
id: string;
name: PartsIndexProductName;
originalName: string;
code: string;
barcodes: string[];
brand: PartsIndexBrand;
description: string;
parameters: {
id: string;
name: string;
params: PartsIndexParameter[];
}[];
images: string[];
links: any[];
groups: {
main: Array<{
id: string;
name: string;
level: number;
}>;
additional: Array<Array<{
id: string;
name: string;
level: number;
}>>;
};
}
export interface PartsIndexEntityInfoResponse {
list: PartsIndexEntityInfo[];
}
export interface PartsIndexEntityInfoVariables {
code: string;
brand?: string;
lang?: string;
}