import React, { useState, useEffect } from "react"; import { useCart } from "@/contexts/CartContext"; import { useFavorites } from "@/contexts/FavoritesContext"; import toast from "react-hot-toast"; import CartIcon from "./CartIcon"; const INITIAL_OFFERS_LIMIT = 5; interface CoreProductCardOffer { id?: string; productId?: string; offerKey?: string; pcs: string; days: string; recommended?: boolean; price: string; count: string; isExternal?: boolean; currency?: string; warehouse?: string; supplier?: string; deliveryTime?: number; } interface CoreProductCardProps { brand: string; article: string; name: string; image?: string; offers: CoreProductCardOffer[]; showMoreText?: string; isAnalog?: boolean; isLoadingOffers?: boolean; onLoadOffers?: () => void; partsIndexPowered?: boolean; } const CoreProductCard: React.FC = ({ brand, article, name, image, offers, showMoreText, isAnalog = false, isLoadingOffers = false, onLoadOffers, partsIndexPowered = false }) => { const { addItem } = useCart(); const { addToFavorites, removeFromFavorites, isFavorite, favorites } = useFavorites(); const [visibleOffersCount, setVisibleOffersCount] = useState(INITIAL_OFFERS_LIMIT); const [quantities, setQuantities] = useState<{ [key: number]: number }>( offers.reduce((acc, _, index) => ({ ...acc, [index]: 1 }), {}) ); const [inputValues, setInputValues] = useState<{ [key: number]: string }>( offers.reduce((acc, _, index) => ({ ...acc, [index]: "1" }), {}) ); const [quantityErrors, setQuantityErrors] = useState<{ [key: number]: string }>({}); useEffect(() => { setInputValues(offers.reduce((acc, _, index) => ({ ...acc, [index]: "1" }), {})); setQuantities(offers.reduce((acc, _, index) => ({ ...acc, [index]: 1 }), {})); }, [offers.length]); const displayedOffers = offers.slice(0, visibleOffersCount); const hasMoreOffers = visibleOffersCount < offers.length; // Проверяем, есть ли товар в избранном const isItemFavorite = isFavorite( offers[0]?.productId, offers[0]?.offerKey, article, brand ); // Функция для парсинга цены из строки const parsePrice = (priceStr: string): number => { const cleanPrice = priceStr.replace(/[^\d.,]/g, '').replace(',', '.'); return parseFloat(cleanPrice) || 0; }; // Функция для парсинга времени доставки const parseDeliveryTime = (daysStr: string): string => { const match = daysStr.match(/\d+/); return match ? `${match[0]} дней` : daysStr; }; // Функция для парсинга количества в наличии const parseStock = (stockStr: string): number => { const match = stockStr.match(/\d+/); return match ? parseInt(match[0]) : 0; }; const handleInputChange = (idx: number, val: string) => { setInputValues(prev => ({ ...prev, [idx]: val })); if (val === "") return; const valueNum = Math.max(1, parseInt(val, 10) || 1); setQuantities(prev => ({ ...prev, [idx]: valueNum })); }; const handleInputBlur = (idx: number) => { if (inputValues[idx] === "") { setInputValues(prev => ({ ...prev, [idx]: "1" })); setQuantities(prev => ({ ...prev, [idx]: 1 })); } }; const handleMinus = (idx: number) => { setQuantities(prev => { const newVal = Math.max(1, (prev[idx] || 1) - 1); setInputValues(vals => ({ ...vals, [idx]: newVal.toString() })); return { ...prev, [idx]: newVal }; }); }; const handlePlus = (idx: number, maxCount?: number) => { setQuantities(prev => { let newVal = (prev[idx] || 1) + 1; if (maxCount !== undefined) newVal = Math.min(newVal, maxCount); setInputValues(vals => ({ ...vals, [idx]: newVal.toString() })); return { ...prev, [idx]: newVal }; }); }; const handleAddToCart = async (offer: CoreProductCardOffer, index: number) => { const quantity = quantities[index] || 1; const availableStock = parseStock(offer.pcs); const numericPrice = parsePrice(offer.price); const result = await addItem({ productId: offer.productId, offerKey: offer.offerKey, name: name, description: `${brand} ${article} - ${name}`, brand: brand, article: article, price: numericPrice, currency: offer.currency || 'RUB', quantity: quantity, stock: availableStock, // передаем информацию о наличии deliveryTime: parseDeliveryTime(offer.days), warehouse: offer.warehouse || 'Склад', supplier: offer.supplier || (offer.isExternal ? 'AutoEuro' : 'Protek'), isExternal: offer.isExternal || false, image: image, }); if (result.success) { // Показываем тоастер вместо alert toast.success(
Товар добавлен в корзину!
{`${brand} ${article} (${quantity} шт.)`}
, { duration: 3000, icon: , } ); } else { // Показываем ошибку toast.error(result.error || 'Ошибка при добавлении товара в корзину'); } }; // Обработчик клика по сердечку const handleFavoriteClick = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (isItemFavorite) { // Находим товар в избранном и удаляем по правильному ID const favoriteItem = favorites.find((item: any) => { // Проверяем по разным комбинациям идентификаторов if (offers[0]?.productId && item.productId === offers[0].productId) return true; if (offers[0]?.offerKey && item.offerKey === offers[0].offerKey) return true; if (item.article === article && item.brand === brand) return true; return false; }); if (favoriteItem) { removeFromFavorites(favoriteItem.id); } } else { // Добавляем в избранное const bestOffer = offers[0]; // Берем первое предложение как лучшее const numericPrice = bestOffer ? parsePrice(bestOffer.price) : 0; addToFavorites({ productId: bestOffer?.productId, offerKey: bestOffer?.offerKey, name: name, brand: brand, article: article, price: numericPrice, currency: bestOffer?.currency || 'RUB', image: image }); } }; if (isLoadingOffers) { return (
info

{brand}

{article}

{name}

Загрузка предложений...

); } if (!offers || offers.length === 0) { return (
info

{brand}

{article}

{name}
{image && (
{name} {partsIndexPowered && (
powered by Parts Index
)}
)}
{onLoadOffers ? ( ) : (

Предложений не найдено.

)}
); } return ( <>
info

{brand}

{article}

{name}
{image && (
{name} {partsIndexPowered && (
powered by Parts Index
)}
)}
Наличие
Доставка
Цена
{displayedOffers.map((offer, idx) => { const isLast = idx === displayedOffers.length - 1; const maxCount = parseStock(offer.pcs); return (
{offer.pcs}
{offer.days}
{offer.recommended && ( <>
Рекомендуем
)}
{offer.price}
handleMinus(idx)} style={{ cursor: 'pointer' }} aria-label="Уменьшить количество" tabIndex={0} onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && handleMinus(idx)} role="button" >
handleInputChange(idx, e.target.value)} onBlur={() => handleInputBlur(idx)} className="text-block-26 w-full text-center outline-none" aria-label="Количество" />
handlePlus(idx, maxCount)} style={{ cursor: 'pointer' }} aria-label="Увеличить количество" tabIndex={0} onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && handlePlus(idx, maxCount)} role="button" >
); })} {hasMoreOffers || visibleOffersCount > INITIAL_OFFERS_LIMIT ? (
{ if (hasMoreOffers) { setVisibleOffersCount(prev => Math.min(prev + 10, offers.length)); } else { setVisibleOffersCount(INITIAL_OFFERS_LIMIT); } }} style={{ cursor: 'pointer' }} tabIndex={0} role="button" aria-label={hasMoreOffers ? `Еще ${offers.length - visibleOffersCount} предложений` : 'Скрыть предложения'} onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { if (hasMoreOffers) { setVisibleOffersCount(prev => Math.min(prev + 10, offers.length)); } else { setVisibleOffersCount(INITIAL_OFFERS_LIMIT); } } }} >
{hasMoreOffers ? `Еще ${offers.length - visibleOffersCount} предложений` : 'Скрыть'}
) : null}
); }; export default CoreProductCard;