164 lines
5.9 KiB
TypeScript
164 lines
5.9 KiB
TypeScript
import React, { useRef } from "react";
|
||
import { useQuery } from '@apollo/client';
|
||
import ArticleCard from "../ArticleCard";
|
||
import CatalogProductCardSkeleton from "../CatalogProductCardSkeleton";
|
||
import { GET_NEW_ARRIVALS } from "@/lib/graphql";
|
||
import { PartsAPIArticle } from "@/types/partsapi";
|
||
|
||
const SCROLL_AMOUNT = 340; // px, ширина одной карточки + отступ
|
||
|
||
// Интерфейс для товара из GraphQL
|
||
interface Product {
|
||
id: string;
|
||
name: string;
|
||
slug: string;
|
||
article?: string;
|
||
brand?: string;
|
||
retailPrice?: number;
|
||
wholesalePrice?: number;
|
||
createdAt: string;
|
||
images: Array<{
|
||
id: string;
|
||
url: string;
|
||
alt?: string;
|
||
order: number;
|
||
}>;
|
||
categories: Array<{
|
||
id: string;
|
||
name: string;
|
||
slug: string;
|
||
}>;
|
||
}
|
||
|
||
// Функция для преобразования Product в PartsAPIArticle
|
||
const transformProductToArticle = (product: Product, index: number): PartsAPIArticle => {
|
||
return {
|
||
artId: product.id,
|
||
artArticleNr: product.article || `PROD-${product.id}`,
|
||
artSupBrand: product.brand || 'Unknown Brand',
|
||
supBrand: product.brand || 'Unknown Brand',
|
||
supId: index + 1,
|
||
productGroup: product.categories?.[0]?.name || product.name,
|
||
ptId: index + 1,
|
||
};
|
||
};
|
||
|
||
const NewArrivalsSection: React.FC = () => {
|
||
const scrollRef = useRef<HTMLDivElement>(null);
|
||
|
||
// Получаем новые поступления через GraphQL
|
||
const { data, loading, error } = useQuery(GET_NEW_ARRIVALS, {
|
||
variables: { limit: 8 }
|
||
});
|
||
|
||
const scrollLeft = () => {
|
||
if (scrollRef.current) {
|
||
scrollRef.current.scrollBy({ left: -SCROLL_AMOUNT, behavior: 'smooth' });
|
||
}
|
||
};
|
||
|
||
const scrollRight = () => {
|
||
if (scrollRef.current) {
|
||
scrollRef.current.scrollBy({ left: SCROLL_AMOUNT, behavior: 'smooth' });
|
||
}
|
||
};
|
||
|
||
// Преобразуем данные для ArticleCard
|
||
const newArrivalsArticles = data?.newArrivals?.map((product: Product, index: number) =>
|
||
transformProductToArticle(product, index)
|
||
) || [];
|
||
|
||
// Получаем изображения для товаров
|
||
const getProductImage = (product: Product): string => {
|
||
if (product.images && product.images.length > 0) {
|
||
return product.images[0].url;
|
||
}
|
||
return "/images/162615.webp"; // fallback изображение
|
||
};
|
||
|
||
return (
|
||
<section className="main">
|
||
<div className="w-layout-blockcontainer container w-container">
|
||
<div className="w-layout-vflex inbt">
|
||
<div className="w-layout-hflex flex-block-31">
|
||
<h2 className="heading-4">Новое поступление</h2>
|
||
</div>
|
||
<div className="carousel-row">
|
||
<button
|
||
className="carousel-arrow carousel-arrow-left"
|
||
onClick={scrollLeft}
|
||
aria-label="Прокрутить влево"
|
||
style={{ cursor: 'pointer' }}
|
||
>
|
||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="16" cy="16" r="16" fill="#F3F4F6"/>
|
||
<path d="M19.5 24L12.5 16L19.5 8" stroke="#222" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
|
||
<div className="w-layout-hflex core-product-search carousel-scroll" ref={scrollRef}>
|
||
{loading ? (
|
||
// Показываем скелетоны во время загрузки
|
||
Array(8).fill(0).map((_, index) => (
|
||
<CatalogProductCardSkeleton key={`skeleton-${index}`} />
|
||
))
|
||
) : error ? (
|
||
// Показываем сообщение об ошибке
|
||
<div className="error-message" style={{
|
||
padding: '20px',
|
||
textAlign: 'center',
|
||
color: '#666',
|
||
minWidth: '300px'
|
||
}}>
|
||
<p>Не удалось загрузить новые поступления</p>
|
||
<p style={{ fontSize: '14px', marginTop: '8px' }}>
|
||
{error.message}
|
||
</p>
|
||
</div>
|
||
) : newArrivalsArticles.length > 0 ? (
|
||
// Показываем товары
|
||
newArrivalsArticles.map((article: PartsAPIArticle, index: number) => {
|
||
const product = data.newArrivals[index];
|
||
const image = getProductImage(product);
|
||
|
||
return (
|
||
<ArticleCard
|
||
key={article.artId || `article-${index}`}
|
||
article={article}
|
||
index={index}
|
||
image={image}
|
||
/>
|
||
);
|
||
})
|
||
) : (
|
||
// Показываем сообщение о том, что товаров нет
|
||
<div className="no-products-message" style={{
|
||
padding: '20px',
|
||
textAlign: 'center',
|
||
color: '#666',
|
||
minWidth: '300px'
|
||
}}>
|
||
<p>Пока нет новых поступлений</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<button
|
||
className="carousel-arrow carousel-arrow-right"
|
||
onClick={scrollRight}
|
||
aria-label="Прокрутить вправо"
|
||
style={{ cursor: 'pointer' }}
|
||
>
|
||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="16" cy="16" r="16" fill="#F3F4F6"/>
|
||
<path d="M12.5 8L19.5 16L12.5 24" stroke="#222" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
};
|
||
|
||
export default NewArrivalsSection;
|