diff --git a/src/components/BestPriceItem.tsx b/src/components/BestPriceItem.tsx index 8054f0a..829dcc8 100644 --- a/src/components/BestPriceItem.tsx +++ b/src/components/BestPriceItem.tsx @@ -109,7 +109,6 @@ const BestPriceItem: React.FC = ({ if (favoriteItem) { removeFromFavorites(favoriteItem.id); - toast.success('Товар удален из избранного'); } } else { // Добавляем в избранное diff --git a/src/components/Pagination.tsx b/src/components/Pagination.tsx new file mode 100644 index 0000000..77162f6 --- /dev/null +++ b/src/components/Pagination.tsx @@ -0,0 +1,148 @@ +import React from 'react'; + +interface PaginationProps { + currentPage: number; + totalPages: number; + onPageChange: (page: number) => void; + className?: string; + showPageInfo?: boolean; +} + +const Pagination: React.FC = ({ + currentPage, + totalPages, + onPageChange, + className = "", + showPageInfo = true +}) => { + const generatePageNumbers = () => { + const pages: (number | string)[] = []; + const delta = 2; // Количество страниц вокруг текущей + + if (totalPages <= 7) { + // Если страниц мало, показываем все + for (let i = 1; i <= totalPages; i++) { + pages.push(i); + } + } else { + // Всегда показываем первую страницу + pages.push(1); + + if (currentPage > delta + 2) { + pages.push('...'); + } + + // Показываем страницы вокруг текущей + const start = Math.max(2, currentPage - delta); + const end = Math.min(totalPages - 1, currentPage + delta); + + for (let i = start; i <= end; i++) { + pages.push(i); + } + + if (currentPage < totalPages - delta - 1) { + pages.push('...'); + } + + // Всегда показываем последнюю страницу + if (totalPages > 1) { + pages.push(totalPages); + } + } + + return pages; + }; + + const pageNumbers = generatePageNumbers(); + + if (totalPages <= 1) { + return null; + } + + return ( +
+ {/* Основные кнопки пагинации */} +
+ {/* Предыдущая страница */} + + + {/* Номера страниц */} + {pageNumbers.map((page, index) => ( + + {page === '...' ? ( + + + + + + + + ) : ( + + )} + + ))} + + {/* Следующая страница */} + +
+ + {/* Информация о страницах */} + {showPageInfo && ( +
+ Страница {currentPage} из {totalPages} +
+ )} +
+ ); +}; + +export default Pagination; \ No newline at end of file diff --git a/src/components/TopSalesItem.tsx b/src/components/TopSalesItem.tsx index b03ce0c..d009c83 100644 --- a/src/components/TopSalesItem.tsx +++ b/src/components/TopSalesItem.tsx @@ -77,7 +77,6 @@ const TopSalesItem: React.FC = ({ }); if (favoriteItem) { removeFromFavorites(favoriteItem.id); - toast.success('Товар удален из избранного'); } } else { const numericPrice = parsePrice(price); diff --git a/src/components/index/NewArrivalsSection.tsx b/src/components/index/NewArrivalsSection.tsx index e1ef8d8..6ef8070 100644 --- a/src/components/index/NewArrivalsSection.tsx +++ b/src/components/index/NewArrivalsSection.tsx @@ -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(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 (
@@ -60,18 +84,71 @@ const NewArrivalsSection: React.FC = () => {

Новое поступление

- +
- {newArrivalsArticles.map((article, i) => ( - - ))} + {loading ? ( + // Показываем скелетоны во время загрузки + Array(8).fill(0).map((_, index) => ( + + )) + ) : error ? ( + // Показываем сообщение об ошибке +
+

Не удалось загрузить новые поступления

+

+ {error.message} +

+
+ ) : newArrivalsArticles.length > 0 ? ( + // Показываем товары + newArrivalsArticles.map((article: PartsAPIArticle, index: number) => { + const product = data.newArrivals[index]; + const image = getProductImage(product); + + return ( + + ); + }) + ) : ( + // Показываем сообщение о том, что товаров нет +
+

Пока нет новых поступлений

+
+ )}
-