Удалены уведомления об удалении товара из избранного в компонентах BestPriceItem и TopSalesItem. Добавлен новый запрос GraphQL для получения новых поступлений, реализована логика загрузки и отображения данных в компоненте NewArrivalsSection. Обновлены компоненты ProfileHistoryItem и ProfileHistoryMain для поддержки новых пропсов и пагинации. Улучшено взаимодействие с пользователем через обработку кликов и отображение состояния загрузки.
This commit is contained in:
@ -1,57 +1,81 @@
|
||||
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 newArrivalsArticles: PartsAPIArticle[] = [
|
||||
{
|
||||
artId: "1",
|
||||
artArticleNr: "6CT-60L",
|
||||
artSupBrand: "TYUMEN BATTERY",
|
||||
supBrand: "TYUMEN BATTERY",
|
||||
supId: 1,
|
||||
productGroup: "Аккумуляторная батарея",
|
||||
ptId: 1,
|
||||
},
|
||||
{
|
||||
artId: "2",
|
||||
artArticleNr: "A0001",
|
||||
artSupBrand: "Borsehung",
|
||||
supBrand: "Borsehung",
|
||||
supId: 2,
|
||||
productGroup: "Масляный фильтр",
|
||||
ptId: 2,
|
||||
},
|
||||
// ...добавьте еще 6 статей для примера
|
||||
...Array(6).fill(0).map((_, i) => ({
|
||||
artId: `${i+3}`,
|
||||
artArticleNr: `ART${i+3}`,
|
||||
artSupBrand: `Brand${i+3}`,
|
||||
supBrand: `Brand${i+3}`,
|
||||
supId: i+3,
|
||||
productGroup: `Product Group ${i+3}`,
|
||||
ptId: i+3,
|
||||
}))
|
||||
];
|
||||
|
||||
const imagePath = "images/162615.webp";
|
||||
|
||||
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">
|
||||
@ -60,18 +84,71 @@ const NewArrivalsSection: React.FC = () => {
|
||||
<h2 className="heading-4">Новое поступление</h2>
|
||||
</div>
|
||||
<div className="carousel-row">
|
||||
<button className="carousel-arrow carousel-arrow-left" onClick={scrollLeft} aria-label="Прокрутить влево">
|
||||
<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}>
|
||||
{newArrivalsArticles.map((article, i) => (
|
||||
<ArticleCard key={article.artId || i} article={{ ...article, artId: article.artId }} index={i} image={imagePath} />
|
||||
))}
|
||||
{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="Прокрутить вправо">
|
||||
|
||||
<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"/>
|
||||
|
Reference in New Issue
Block a user