Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
f4facf146c | |||
80ed5826e2 | |||
9adc737028 |
@ -5,6 +5,7 @@ import { useArticleImage } from '@/hooks/useArticleImage';
|
|||||||
import { useCatalogPrices } from '@/hooks/useCatalogPrices';
|
import { useCatalogPrices } from '@/hooks/useCatalogPrices';
|
||||||
import { PartsAPIArticle } from '@/types/partsapi';
|
import { PartsAPIArticle } from '@/types/partsapi';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
import { useCart } from '@/contexts/CartContext';
|
||||||
|
|
||||||
interface ArticleCardProps {
|
interface ArticleCardProps {
|
||||||
article: PartsAPIArticle;
|
article: PartsAPIArticle;
|
||||||
@ -17,6 +18,9 @@ const ArticleCard: React.FC<ArticleCardProps> = memo(({ article, index, onVisibi
|
|||||||
const [shouldShow, setShouldShow] = useState(false);
|
const [shouldShow, setShouldShow] = useState(false);
|
||||||
const [isChecking, setIsChecking] = useState(true);
|
const [isChecking, setIsChecking] = useState(true);
|
||||||
|
|
||||||
|
// Cart context
|
||||||
|
const { isInCart: isItemInCart } = useCart();
|
||||||
|
|
||||||
// Используем хук для получения изображения
|
// Используем хук для получения изображения
|
||||||
const { imageUrl, isLoading: imageLoading, error } = useArticleImage(article.artId, {
|
const { imageUrl, isLoading: imageLoading, error } = useArticleImage(article.artId, {
|
||||||
enabled: !!article.artId
|
enabled: !!article.artId
|
||||||
@ -85,6 +89,8 @@ const ArticleCard: React.FC<ArticleCardProps> = memo(({ article, index, onVisibi
|
|||||||
const title = [brandName || 'N/A', articleNumber || 'N/A'].filter(part => part !== 'N/A').join(', ');
|
const title = [brandName || 'N/A', articleNumber || 'N/A'].filter(part => part !== 'N/A').join(', ');
|
||||||
const brand = brandName || 'Unknown';
|
const brand = brandName || 'Unknown';
|
||||||
let priceText = 'от 17 087 ₽';
|
let priceText = 'от 17 087 ₽';
|
||||||
|
const isInCartFlag = isItemInCart(undefined, undefined, articleNumber, brandName);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CatalogProductCard
|
<CatalogProductCard
|
||||||
image={fallbackImage}
|
image={fallbackImage}
|
||||||
@ -97,6 +103,7 @@ const ArticleCard: React.FC<ArticleCardProps> = memo(({ article, index, onVisibi
|
|||||||
brandName={brandName}
|
brandName={brandName}
|
||||||
artId={article.artId}
|
artId={article.artId}
|
||||||
onAddToCart={() => {}}
|
onAddToCart={() => {}}
|
||||||
|
isInCart={isInCartFlag}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -109,6 +116,8 @@ const ArticleCard: React.FC<ArticleCardProps> = memo(({ article, index, onVisibi
|
|||||||
|
|
||||||
const brand = brandName || 'Unknown';
|
const brand = brandName || 'Unknown';
|
||||||
|
|
||||||
|
const isInCartFlag = isItemInCart(undefined, undefined, articleNumber, brandName);
|
||||||
|
|
||||||
// Формируем цену для отображения
|
// Формируем цену для отображения
|
||||||
let priceText = '';
|
let priceText = '';
|
||||||
if (priceData.isLoading) {
|
if (priceData.isLoading) {
|
||||||
@ -144,6 +153,7 @@ const ArticleCard: React.FC<ArticleCardProps> = memo(({ article, index, onVisibi
|
|||||||
brandName={brandName}
|
brandName={brandName}
|
||||||
artId={article.artId}
|
artId={article.artId}
|
||||||
onAddToCart={handleAddToCart}
|
onAddToCart={handleAddToCart}
|
||||||
|
isInCart={isInCartFlag}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -28,10 +28,15 @@ const BestPriceItem: React.FC<BestPriceItemProps> = ({
|
|||||||
onAddToCart,
|
onAddToCart,
|
||||||
isInCart = false,
|
isInCart = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { addItem } = useCart();
|
const { addItem, isInCart: isItemInCart, state: cartState } = useCart();
|
||||||
const { addToFavorites, removeFromFavorites, isFavorite, favorites } = useFavorites();
|
const { addToFavorites, removeFromFavorites, isFavorite, favorites } = useFavorites();
|
||||||
const [localInCart, setLocalInCart] = useState(false);
|
const [localInCart, setLocalInCart] = useState(false);
|
||||||
|
|
||||||
|
// Determine inCart via context if not provided
|
||||||
|
const inCartContext = isItemInCart(productId, undefined, article, brand);
|
||||||
|
|
||||||
|
const inCart = isInCart || inCartContext;
|
||||||
|
|
||||||
// Проверяем, есть ли товар в избранном
|
// Проверяем, есть ли товар в избранном
|
||||||
const isItemFavorite = isFavorite(productId, undefined, article, brand);
|
const isItemFavorite = isFavorite(productId, undefined, article, brand);
|
||||||
|
|
||||||
@ -172,15 +177,16 @@ const BestPriceItem: React.FC<BestPriceItemProps> = ({
|
|||||||
<div className="nameitembp">{title}</div>
|
<div className="nameitembp">{title}</div>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
className="button-icon w-inline-block"
|
className={`button-icon w-inline-block ${inCart || localInCart ? 'in-cart' : ''}`}
|
||||||
onClick={isInCart ? undefined : handleAddToCart}
|
onClick={inCart ? undefined : handleAddToCart}
|
||||||
style={{
|
style={{
|
||||||
cursor: isInCart ? 'default' : (localInCart ? 'default' : 'pointer'),
|
cursor: inCart || localInCart ? 'default' : 'pointer',
|
||||||
background: isInCart ? '#9ca3af' : (localInCart ? '#2563eb' : undefined),
|
textDecoration: 'none',
|
||||||
opacity: isInCart || localInCart ? 0.5 : 1,
|
opacity: inCart || localInCart ? 0.5 : 1,
|
||||||
filter: isInCart || localInCart ? 'grayscale(1)' : 'none'
|
backgroundColor: inCart || localInCart ? '#9ca3af' : undefined
|
||||||
}}
|
}}
|
||||||
aria-label="Добавить в корзину"
|
aria-label={inCart || localInCart ? "Товар уже в корзине" : "Добавить в корзину"}
|
||||||
|
title={inCart || localInCart ? "Товар уже в корзине" : "Добавить в корзину"}
|
||||||
>
|
>
|
||||||
<div className="div-block-26">
|
<div className="div-block-26">
|
||||||
<div className="icon-setting w-embed">
|
<div className="icon-setting w-embed">
|
||||||
|
@ -13,7 +13,7 @@ interface TopSalesItemProps {
|
|||||||
productId?: string;
|
productId?: string;
|
||||||
onAddToCart?: (e: React.MouseEvent) => void;
|
onAddToCart?: (e: React.MouseEvent) => void;
|
||||||
discount?: string; // Новый пропс для лейбла/скидки
|
discount?: string; // Новый пропс для лейбла/скидки
|
||||||
// isInCart?: boolean; // Удаляем из пропсов
|
isInCart?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TopSalesItem: React.FC<TopSalesItemProps> = ({
|
const TopSalesItem: React.FC<TopSalesItemProps> = ({
|
||||||
@ -27,15 +27,13 @@ const TopSalesItem: React.FC<TopSalesItemProps> = ({
|
|||||||
discount = 'Топ продаж', // По умолчанию как раньше
|
discount = 'Топ продаж', // По умолчанию как раньше
|
||||||
// isInCart = false, // Удаляем из пропсов
|
// isInCart = false, // Удаляем из пропсов
|
||||||
}) => {
|
}) => {
|
||||||
const { addItem, cartItems = [] } = useCart();
|
const { addItem, isInCart: isItemInCart } = useCart();
|
||||||
const { addToFavorites, removeFromFavorites, isFavorite, favorites } = useFavorites();
|
const { addToFavorites, removeFromFavorites, isFavorite, favorites } = useFavorites();
|
||||||
const [localInCart, setLocalInCart] = useState(false);
|
const [localInCart, setLocalInCart] = useState(false);
|
||||||
|
|
||||||
const isItemFavorite = isFavorite(productId, undefined, article, brand);
|
const isItemFavorite = isFavorite(productId, undefined, article, brand);
|
||||||
const isInCart = cartItems.some(item =>
|
|
||||||
(productId && item.productId === productId) ||
|
const isInCart = isItemInCart(productId, undefined, article, brand);
|
||||||
(article && brand && item.article === article && item.brand === brand)
|
|
||||||
);
|
|
||||||
|
|
||||||
const parsePrice = (priceStr: string): number => {
|
const parsePrice = (priceStr: string): number => {
|
||||||
const cleanPrice = priceStr.replace(/[^\d.,]/g, '').replace(',', '.');
|
const cleanPrice = priceStr.replace(/[^\d.,]/g, '').replace(',', '.');
|
||||||
@ -152,6 +150,7 @@ const TopSalesItem: React.FC<TopSalesItemProps> = ({
|
|||||||
filter: isInCart || localInCart ? 'grayscale(1)' : 'none'
|
filter: isInCart || localInCart ? 'grayscale(1)' : 'none'
|
||||||
}}
|
}}
|
||||||
aria-label={isInCart ? 'В корзине' : (localInCart ? 'Добавлено' : 'Добавить в корзину')}
|
aria-label={isInCart ? 'В корзине' : (localInCart ? 'Добавлено' : 'Добавить в корзину')}
|
||||||
|
title={isInCart ? 'Товар уже в корзине - нажмите для добавления еще' : 'Добавить в корзину'}
|
||||||
>
|
>
|
||||||
<div className="div-block-26">
|
<div className="div-block-26">
|
||||||
<div className="icon-setting w-embed">
|
<div className="icon-setting w-embed">
|
||||||
|
@ -3,10 +3,17 @@ import { useQuery } from '@apollo/client';
|
|||||||
import { GET_PARTSINDEX_CATEGORIES } from '@/lib/graphql';
|
import { GET_PARTSINDEX_CATEGORIES } from '@/lib/graphql';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
|
interface CategoryNavGroup {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
image?: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface CategoryNavItem {
|
interface CategoryNavItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
image?: string;
|
image?: string;
|
||||||
|
groups?: CategoryNavGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const FALLBACK_CATEGORIES: CategoryNavItem[] = [
|
const FALLBACK_CATEGORIES: CategoryNavItem[] = [
|
||||||
@ -38,11 +45,15 @@ const CategoryNavSection: React.FC = () => {
|
|||||||
: FALLBACK_CATEGORIES;
|
: FALLBACK_CATEGORIES;
|
||||||
|
|
||||||
const handleCategoryClick = (category: CategoryNavItem) => {
|
const handleCategoryClick = (category: CategoryNavItem) => {
|
||||||
|
// Получаем первую доступную группу для навигации в PartsIndex режим
|
||||||
|
const firstGroupId = category.groups && category.groups.length > 0 ? category.groups[0].id : undefined;
|
||||||
|
|
||||||
router.push({
|
router.push({
|
||||||
pathname: '/catalog',
|
pathname: '/catalog',
|
||||||
query: {
|
query: {
|
||||||
categoryId: category.id,
|
partsIndexCatalog: category.id,
|
||||||
categoryName: encodeURIComponent(category.name)
|
categoryName: encodeURIComponent(category.name),
|
||||||
|
...(firstGroupId && { partsIndexCategory: firstGroupId })
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -23,7 +23,7 @@ const SCROLL_AMOUNT = 340; // px, ширина одной карточки + о
|
|||||||
|
|
||||||
const TopSalesSection: React.FC = () => {
|
const TopSalesSection: React.FC = () => {
|
||||||
const { data, loading, error } = useQuery(GET_TOP_SALES_PRODUCTS);
|
const { data, loading, error } = useQuery(GET_TOP_SALES_PRODUCTS);
|
||||||
const { cartItems = [] } = useCart();
|
const { isInCart: isItemInCart } = useCart();
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const scrollLeft = () => {
|
const scrollLeft = () => {
|
||||||
@ -214,7 +214,7 @@ const TopSalesSection: React.FC = () => {
|
|||||||
|
|
||||||
const title = product.name;
|
const title = product.name;
|
||||||
const brand = product.brand || 'Неизвестный бренд';
|
const brand = product.brand || 'Неизвестный бренд';
|
||||||
const isInCart = cartItems.some(cartItem => cartItem.productId === product.id);
|
const isInCart = isItemInCart(product.id, undefined, product.article, brand);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TopSalesItem
|
<TopSalesItem
|
||||||
|
@ -44,7 +44,7 @@ export default function Catalog() {
|
|||||||
const MAX_BRANDS_DISPLAY = 24; // Сколько брендов показывать изначально
|
const MAX_BRANDS_DISPLAY = 24; // Сколько брендов показывать изначально
|
||||||
const [visibleCount, setVisibleCount] = useState(ITEMS_PER_PAGE);
|
const [visibleCount, setVisibleCount] = useState(ITEMS_PER_PAGE);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { addItem } = useCart();
|
const { addItem, isInCart: isItemInCart } = useCart();
|
||||||
const {
|
const {
|
||||||
partsApiCategory: strId,
|
partsApiCategory: strId,
|
||||||
categoryName,
|
categoryName,
|
||||||
@ -1141,6 +1141,8 @@ export default function Catalog() {
|
|||||||
const productForPrice = { id: entity.id, code: entity.code, brand: entity.brand.name };
|
const productForPrice = { id: entity.id, code: entity.code, brand: entity.brand.name };
|
||||||
const priceData = getPrice(productForPrice);
|
const priceData = getPrice(productForPrice);
|
||||||
const isLoadingPriceData = isLoadingPrice(productForPrice);
|
const isLoadingPriceData = isLoadingPrice(productForPrice);
|
||||||
|
// Fallback cart check via frontend context
|
||||||
|
const inCartFallback = isItemInCart(entity.id, priceData?.offerKey, entity.code, entity.brand.name);
|
||||||
|
|
||||||
// Определяем цену для отображения (все товары уже отфильтрованы на сервере)
|
// Определяем цену для отображения (все товары уже отфильтрованы на сервере)
|
||||||
let displayPrice = "";
|
let displayPrice = "";
|
||||||
@ -1174,7 +1176,7 @@ export default function Catalog() {
|
|||||||
productId={entity.id}
|
productId={entity.id}
|
||||||
artId={entity.id}
|
artId={entity.id}
|
||||||
offerKey={priceData?.offerKey}
|
offerKey={priceData?.offerKey}
|
||||||
isInCart={priceData?.isInCart}
|
isInCart={priceData?.isInCart || inCartFallback}
|
||||||
onAddToCart={async () => {
|
onAddToCart={async () => {
|
||||||
// Если цена не загружена, загружаем её и добавляем в корзину
|
// Если цена не загружена, загружаем её и добавляем в корзину
|
||||||
if (!priceData && !isLoadingPriceData) {
|
if (!priceData && !isLoadingPriceData) {
|
||||||
|
Reference in New Issue
Block a user