all cart #8

Merged
egortriston merged 1 commits from frontcart into main 2025-06-30 00:40:37 +03:00
9 changed files with 310 additions and 136 deletions

View File

@ -16,6 +16,8 @@ interface CartItemProps {
onComment: (comment: string) => void;
onCountChange?: (count: number) => void;
onRemove?: () => void;
isSummaryStep?: boolean;
itemNumber?: number;
}
const CartItem: React.FC<CartItemProps> = ({
@ -34,9 +36,14 @@ const CartItem: React.FC<CartItemProps> = ({
onComment,
onCountChange,
onRemove,
isSummaryStep = false,
itemNumber,
}) => (
<div className="w-layout-hflex cart-item">
<div className="w-layout-hflex info-block-search-copy">
{isSummaryStep ? (
<div style={{ marginRight: 12, minWidth: 24, textAlign: 'center', fontWeight: 600, fontSize: 14 }}>{itemNumber}</div>
) : (
<div
className={"div-block-7" + (selected ? " active" : "")}
onClick={onSelect}
@ -48,9 +55,22 @@ const CartItem: React.FC<CartItemProps> = ({
</svg>
)}
</div>
)}
<div className="w-layout-hflex block-name">
<h4 className="heading-9-copy">{name}</h4>
<div className="text-block-21-copy">{description}</div>
<div
className={
"text-block-21-copy" +
(isSummaryStep && itemNumber === 1 ? " border-t-0" : "")
}
style={
isSummaryStep && itemNumber === 1
? { borderTop: 'none' }
: undefined
}
>
{description}
</div>
</div>
<div className="form-block-copy w-form">
<form className="form-copy" onSubmit={e => e.preventDefault()}>
@ -64,6 +84,7 @@ const CartItem: React.FC<CartItemProps> = ({
id="Search-5"
value={comment}
onChange={e => onComment(e.target.value)}
disabled={isSummaryStep}
/>
</form>
<div className="success-message w-form-done">
@ -80,87 +101,95 @@ const CartItem: React.FC<CartItemProps> = ({
<div className="text-block-21-copy-copy">{deliveryDate}</div>
</div>
<div className="w-layout-hflex pcs-cart-s1">
<div
className="minus-plus"
onClick={() => onCountChange && onCountChange(count - 1)}
style={{ cursor: 'pointer' }}
aria-label="Уменьшить количество"
tabIndex={0}
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && onCountChange && onCountChange(count - 1)}
role="button"
>
<div className="pluspcs w-embed">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 10.5V9.5H14V10.5H6Z" fill="currentColor"/>
</svg>
</div>
{isSummaryStep ? (
<div className="text-block-26" style={{ fontWeight: 600, fontSize: 14 }}>{count} шт.</div>
) : (
<>
<div
className="minus-plus"
onClick={() => onCountChange && onCountChange(count - 1)}
style={{ cursor: 'pointer' }}
aria-label="Уменьшить количество"
tabIndex={0}
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && onCountChange && onCountChange(count - 1)}
role="button"
>
<div className="pluspcs w-embed">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 10.5V9.5H14V10.5H6Z" fill="currentColor"/>
</svg>
</div>
</div>
<div className="input-pcs">
<input
type="number"
min={1}
value={count}
onChange={e => {
const value = Math.max(1, parseInt(e.target.value, 10) || 1);
onCountChange && onCountChange(value);
}}
className="text-block-26 w-full text-center outline-none"
aria-label="Количество"
style={{ width: 40 }}
/>
<input
type="number"
min={1}
value={count}
onChange={e => {
const value = Math.max(1, parseInt(e.target.value, 10) || 1);
onCountChange && onCountChange(value);
}}
className="text-block-26 w-full text-center outline-none"
aria-label="Количество"
style={{ width: 40 }}
/>
</div>
<div
className="minus-plus"
onClick={() => onCountChange && onCountChange(count + 1)}
style={{ cursor: 'pointer' }}
aria-label="Увеличить количество"
tabIndex={0}
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && onCountChange && onCountChange(count + 1)}
role="button"
>
<div className="pluspcs w-embed">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 10.5V9.5H14V10.5H6ZM9.5 6H10.5V14H9.5V6Z" fill="currentColor"/>
</svg>
</div>
<div
className="minus-plus"
onClick={() => onCountChange && onCountChange(count + 1)}
style={{ cursor: 'pointer' }}
aria-label="Увеличить количество"
tabIndex={0}
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && onCountChange && onCountChange(count + 1)}
role="button"
>
<div className="pluspcs w-embed">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 10.5V9.5H14V10.5H6ZM9.5 6H10.5V14H9.5V6Z" fill="currentColor"/>
</svg>
</div>
</div>
</>
)}
</div>
<div className="w-layout-hflex flex-block-39-copy-copy">
<h4 className="price-in-cart-s1">{price}</h4>
<div className="price-1-pcs-cart-s1">{pricePerItem}</div>
</div>
{!isSummaryStep && (
<div className="w-layout-hflex control-element">
<div className="favorite-icon w-embed" onClick={onFavorite} style={{ cursor: 'pointer', color: favorite ? '#e53935' : undefined }}>
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 16.5L7.84 15.4929C3.72 11.93 1 9.57248 1 6.69619C1 4.33869 2.936 2.5 5.4 2.5C6.792 2.5 8.128 3.11798 9 4.08692C9.872 3.11798 11.208 2.5 12.6 2.5C15.064 2.5 17 4.33869 17 6.69619C17 9.57248 14.28 11.93 10.16 15.4929L9 16.5Z" fill={favorite ? "#e53935" : "currentColor"} />
</svg>
</div>
<div
className="bdel"
role="button"
tabIndex={0}
aria-label="Удалить из корзины"
onClick={onRemove}
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && onRemove && onRemove()}
style={{ display: 'inline-flex', cursor: 'pointer', transition: 'color 0.2s' }}
onMouseEnter={e => {
const path = e.currentTarget.querySelector('path');
if (path) path.setAttribute('fill', '#ec1c24');
}}
onMouseLeave={e => {
const path = e.currentTarget.querySelector('path');
if (path) path.setAttribute('fill', '#D0D0D0');
}}
>
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"
fill="#D0D0D0"
style={{ transition: 'fill 0.2s' }}
/>
</svg>
</div>
<div
className="bdel"
role="button"
tabIndex={0}
aria-label="Удалить из корзины"
onClick={onRemove}
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && onRemove && onRemove()}
style={{ display: 'inline-flex', cursor: 'pointer', transition: 'color 0.2s' }}
onMouseEnter={e => {
const path = e.currentTarget.querySelector('path');
if (path) path.setAttribute('fill', '#ec1c24');
}}
onMouseLeave={e => {
const path = e.currentTarget.querySelector('path');
if (path) path.setAttribute('fill', '#D0D0D0');
}}
>
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"
fill="#D0D0D0"
style={{ transition: 'fill 0.2s' }}
/>
</svg>
</div>
</div>
)}
</div>
</div>
);

View File

@ -3,7 +3,11 @@ import CartItem from "./CartItem";
import { useCart } from "@/contexts/CartContext";
import { useFavorites } from "@/contexts/FavoritesContext";
const CartList: React.FC = () => {
interface CartListProps {
isSummaryStep?: boolean;
}
const CartList: React.FC<CartListProps> = ({ isSummaryStep = false }) => {
const { state, toggleSelect, updateComment, removeItem, selectAll, removeSelected, updateQuantity } = useCart();
const { addToFavorites, removeFromFavorites, isFavorite, favorites } = useFavorites();
const { items } = state;
@ -25,24 +29,18 @@ const CartList: React.FC = () => {
const handleFavorite = (id: string) => {
const item = items.find(item => item.id === id);
if (!item) return;
const isInFavorites = isFavorite(item.productId, item.offerKey, item.article, item.brand);
if (isInFavorites) {
// Находим товар в избранном по правильному ID
const favoriteItem = favorites.find((fav: any) => {
// Проверяем по разным комбинациям идентификаторов
if (item.productId && fav.productId === item.productId) return true;
if (item.offerKey && fav.offerKey === item.offerKey) return true;
if (fav.article === item.article && fav.brand === item.brand) return true;
return false;
});
if (favoriteItem) {
removeFromFavorites(favoriteItem.id);
}
} else {
// Добавляем в избранное
addToFavorites({
productId: item.productId,
offerKey: item.offerKey,
@ -68,59 +66,73 @@ const CartList: React.FC = () => {
updateQuantity(id, count);
};
// Функция для форматирования цены
const formatPrice = (price: number, currency: string = 'RUB') => {
return `${price.toLocaleString('ru-RU')} ${currency === 'RUB' ? '₽' : currency}`;
};
// На втором шаге показываем только выбранные товары
const displayItems = isSummaryStep ? items.filter(item => item.selected) : items;
return (
<div className="w-layout-vflex flex-block-48">
<div className="w-layout-vflex product-list-cart">
<div className="w-layout-hflex multi-control">
<div className="w-layout-hflex select-all-block" onClick={handleSelectAll} style={{ cursor: 'pointer' }}>
<div
className={"div-block-7" + (allSelected ? " active" : "")}
style={{ marginRight: 8, cursor: 'pointer' }}
>
{allSelected && (
<svg width="14" height="10" viewBox="0 0 14 10" fill="none">
<path d="M2 5.5L6 9L12 2" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
)}
{!isSummaryStep && (
<div className="w-layout-hflex multi-control">
<div className="w-layout-hflex select-all-block" onClick={handleSelectAll} style={{ cursor: 'pointer' }}>
<div
className={"div-block-7" + (allSelected ? " active" : "")}
style={{ marginRight: 8, cursor: 'pointer' }}
>
{allSelected && (
<svg width="14" height="10" viewBox="0 0 14 10" fill="none">
<path d="M2 5.5L6 9L12 2" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
)}
</div>
<div className="text-block-30">Выделить всё</div>
</div>
<div className="w-layout-hflex select-all-block" onClick={handleRemoveSelected} style={{ cursor: 'pointer' }}
onMouseEnter={e => {
const path = (e.currentTarget.querySelector('path'));
if (path) path.setAttribute('fill', '#ec1c24');
}}
onMouseLeave={e => {
const path = (e.currentTarget.querySelector('path'));
if (path) path.setAttribute('fill', '#D0D0D0');
}}
>
<div className="text-block-30">Удалить выбранные</div>
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg" className="image-13">
<path
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"
fill="#D0D0D0"
style={{ transition: 'fill 0.2s' }}
/>
</svg>
</div>
<div className="text-block-30">Выделить всё</div>
</div>
<div className="w-layout-hflex select-all-block" onClick={handleRemoveSelected} style={{ cursor: 'pointer' }}
onMouseEnter={e => {
const path = (e.currentTarget.querySelector('path'));
if (path) path.setAttribute('fill', '#ec1c24');
}}
onMouseLeave={e => {
const path = (e.currentTarget.querySelector('path'));
if (path) path.setAttribute('fill', '#D0D0D0');
}}
>
<div className="text-block-30">Удалить выбранные</div>
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg" className="image-13">
<path
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"
fill="#D0D0D0"
style={{ transition: 'fill 0.2s' }}
/>
</svg>
</div>
</div>
{items.length === 0 ? (
)}
{displayItems.length === 0 ? (
<div className="empty-cart-message" style={{ textAlign: 'center', padding: '2rem', color: '#666' }}>
<p>Ваша корзина пуста</p>
<p>Добавьте товары из каталога</p>
</div>
) : (
items.map((item) => {
displayItems.map((item, idx) => {
const isInFavorites = isFavorite(item.productId, item.offerKey, item.article, item.brand);
return (
<div className="div-block-21" key={item.id}>
<div
className={
"div-block-21" +
(isSummaryStep && idx === 0 ? " border-t-0" : "")
}
style={
isSummaryStep && idx === 0
? { borderTop: 'none' }
: undefined
}
key={item.id}
>
<CartItem
name={item.name}
description={item.description}
@ -137,6 +149,8 @@ const CartList: React.FC = () => {
onComment={(comment) => handleComment(item.id, comment)}
onCountChange={(count) => handleCountChange(item.id, count)}
onRemove={() => handleRemove(item.id)}
isSummaryStep={isSummaryStep}
itemNumber={idx + 1}
/>
</div>
);

View File

@ -26,8 +26,17 @@ const CartList2: React.FC = () => {
<p>Вернитесь на предыдущий шаг и выберите товары</p>
</div>
) : (
selectedItems.map((item) => (
<div className="div-block-21-copy" key={item.id}>
selectedItems.map((item, index) => (
<div
className={
"div-block-21-copy" +
(index === 0 ? " border-t-0" : "")
}
style={
index === 0 ? { borderTop: 'none' } : undefined
}
key={item.id}
>
<div className="w-layout-hflex cart-item-check">
<div className="w-layout-hflex info-block-search">
<div className="text-block-35">{item.quantity}</div>

View File

@ -5,7 +5,12 @@ import { useMutation, useQuery } from "@apollo/client";
import { CREATE_ORDER, CREATE_PAYMENT, GET_CLIENT_ME, GET_CLIENT_DELIVERY_ADDRESSES } from "@/lib/graphql";
import toast from "react-hot-toast";
const CartSummary: React.FC = () => {
interface CartSummaryProps {
step: number;
setStep: (step: number) => void;
}
const CartSummary: React.FC<CartSummaryProps> = ({ step, setStep }) => {
const { state, updateDelivery, updateOrderComment, clearCart } = useCart();
const { summary, delivery, items, orderComment } = state;
const legalEntityDropdownRef = useRef<HTMLDivElement>(null);
@ -16,7 +21,6 @@ const CartSummary: React.FC = () => {
const [error, setError] = useState("");
const [isProcessing, setIsProcessing] = useState(false);
const [showAuthWarning, setShowAuthWarning] = useState(false);
const [step, setStep] = useState(1);
// Новые состояния для первого шага
const [selectedLegalEntity, setSelectedLegalEntity] = useState<string>("");
@ -135,25 +139,20 @@ const CartSummary: React.FC = () => {
toast.error('Пожалуйста, введите имя получателя');
return;
}
if (!recipientPhone.trim()) {
toast.error('Пожалуйста, введите телефон получателя');
return;
}
if (!selectedDeliveryAddress.trim()) {
toast.error('Пожалуйста, выберите адрес доставки');
return;
}
// Обновляем данные доставки без стоимости
updateDelivery({
address: selectedDeliveryAddress,
cost: 0, // Стоимость включена в товары
cost: 0,
date: 'Включена в стоимость товаров',
time: 'Способ доставки указан в адресе'
});
setStep(2);
};
@ -894,7 +893,7 @@ const CartSummary: React.FC = () => {
{error && <div style={{ color: 'red', marginTop: 10 }}>{error}</div>}
{/* Кнопка "Назад" */}
{/* <button
<button
onClick={handleBackToStep1}
style={{
background: 'none',
@ -908,7 +907,7 @@ const CartSummary: React.FC = () => {
}}
>
Назад к настройкам доставки
</button> */}
</button>
<div className="w-layout-hflex privacy-consent" style={{ cursor: 'pointer' }} onClick={() => setConsent((v) => !v)}>
<div

View File

@ -92,7 +92,7 @@ const ProfileHistoryItem: React.FC<ProfileHistoryItemProps> = ({
const path = e.currentTarget.querySelector('path');
if (path) path.setAttribute('fill', '#D0D0D0');
}}
>
>
<svg width="16" height="16" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.625 17.5C4.14375 17.5 3.73192 17.3261 3.3895 16.9782C3.04708 16.6304 2.87558 16.2117 2.875 15.7222V4.16667H2V2.38889H6.375V1.5H11.625V2.38889H16V4.16667H15.125V15.7222C15.125 16.2111 14.9538 16.6298 14.6114 16.9782C14.269 17.3267 13.8568 17.5006 13.375 17.5H4.625ZM6.375 13.9444H8.125V5.94444H6.375V13.9444ZM9.875 13.9444H11.625V5.94444H9.875V13.9444Z"

View File

@ -180,12 +180,42 @@ const VinLeftbar: React.FC<VinLeftbarProps> = ({ vehicleInfo, onSearchResults, o
</div>
);
// === Полнотекстовый поиск деталей (аналогично FulltextSearchSection) ===
const [fulltextQuery, setFulltextQuery] = useState('');
const [executeFulltextSearch, { data: fulltextData, loading: fulltextLoading, error: fulltextError }] = useLazyQuery(SEARCH_LAXIMO_FULLTEXT, { errorPolicy: 'all' });
const handleFulltextSearch = () => {
if (!fulltextQuery.trim()) return;
if (!ssd || ssd.trim() === '') {
console.error('SSD обязателен для поиска по названию');
return;
}
executeFulltextSearch({
variables: {
catalogCode,
vehicleId,
searchText: fulltextQuery.trim(),
ssd
}
});
};
const handleFulltextKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
e.preventDefault();
handleFulltextSearch();
}
};
const fulltextResults = fulltextData?.laximoFulltextSearch?.details || [];
return (
<div className="w-layout-vflex vinleftbar">
{/* === Форма полнотекстового поиска === */}
<div className="div-block-2">
<div className="form-block w-form">
<form id="vin-form-search" name="vin-form-search" data-name="vin-form-search" action="#" method="post" className="form">
<a href="#" className="link-block-3 w-inline-block" onClick={e => { e.preventDefault(); if (!ssd || ssd.trim() === '') { return; } handleSearch(); }}>
<form id="vin-form-search" name="vin-form-search" data-name="vin-form-search" action="#" method="post" className="form" onSubmit={e => { e.preventDefault(); handleFulltextSearch(); }}>
<a href="#" className="link-block-3 w-inline-block" onClick={e => { e.preventDefault(); if (!ssd || ssd.trim() === '') { return; } handleFulltextSearch(); }}>
<div className="code-embed-6 w-embed">
{/* SVG */}
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -203,14 +233,95 @@ const VinLeftbar: React.FC<VinLeftbarProps> = ({ vehicleInfo, onSearchResults, o
type="text"
id="VinSearchInput"
required
value={searchQuery}
onChange={e => setSearchQuery(e.target.value)}
onKeyDown={handleKeyDown}
disabled={loading}
value={fulltextQuery}
onChange={e => setFulltextQuery(e.target.value)}
onKeyDown={handleFulltextKeyDown}
disabled={fulltextLoading}
/>
<button
type="button"
onClick={handleFulltextSearch}
disabled={!fulltextQuery.trim() || fulltextLoading || !ssd || ssd.trim() === ''}
className="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors ml-2"
>
{fulltextLoading ? 'Поиск...' : 'Найти'}
</button>
</form>
{/* Варианты отображения: предупреждение, ошибка, подсказки, результаты */}
{(!ssd || ssd.trim() === '') && (
<div className="mt-3 p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
<div className="flex">
<svg className="h-5 w-5 text-yellow-400 flex-shrink-0" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
<div className="ml-3">
<h3 className="text-sm font-medium text-yellow-800">
Полнотекстовый поиск недоступен
</h3>
<p className="text-sm text-yellow-700 mt-1">
Для поиска по названию деталей необходимо сначала выбрать конкретный автомобиль через поиск по VIN или мастер подбора.
</p>
</div>
</div>
</div>
)}
{fulltextError && (
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mt-3">
<div className="flex">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-red-800">
Ошибка поиска
</h3>
<div className="mt-2 text-sm text-red-700">
<p>{fulltextError.message}</p>
</div>
</div>
</div>
</div>
)}
{fulltextResults && (
<div className="bg-white border border-gray-200 rounded-lg overflow-hidden mt-3">
<div className="px-6 py-4 bg-gray-50 border-b border-gray-200">
<h3 className="text-lg font-medium text-gray-900">
Результаты поиска: "{fulltextQuery}"
</h3>
<p className="text-sm text-gray-600 mt-1">
Найдено {fulltextResults.length} деталей
</p>
</div>
{fulltextResults.length > 0 ? (
<div className="space-y-4 p-6">
<div className="text-sm text-blue-700 bg-blue-50 border border-blue-200 rounded-lg p-3">
💡 Нажмите на карточку детали для поиска предложений и цен. Используйте кнопку "Показать применимость" для просмотра применения в автомобиле.
</div>
{fulltextResults.map((detail: any, index: number) => (
<VinPartCard
key={`${detail.oem}-${index}`}
n={index + 1}
name={detail.name}
oem={detail.oem}
/>
))}
</div>
) : (
<div className="px-6 py-8 text-center">
<svg className="w-12 h-12 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<p className="text-gray-600">
По запросу "{fulltextQuery}" ничего не найдено
</p>
<p className="text-sm text-gray-500 mt-1">
Попробуйте изменить поисковый запрос
</p>
</div>
)}
</div>
)}
</div>
</div>
<div className="w-layout-vflex flex-block-113">

View File

@ -7,8 +7,10 @@ import CartSummary from "@/components/CartSummary";
import CartRecommended from "../components/CartRecommended";
import CatalogSubscribe from "@/components/CatalogSubscribe";
import MobileMenuBottomSection from "@/components/MobileMenuBottomSection";
import React, { useState } from "react";
export default function CartPage() {
const [step, setStep] = useState(1);
return (
<><Head>
@ -26,8 +28,8 @@ export default function CartPage() {
<div className="w-layout-blockcontainer container w-container">
<div className="w-layout-vflex cart-list">
<div className="w-layout-hflex core-product-card">
<CartList />
<CartSummary />
<CartList isSummaryStep={step === 2} />
<CartSummary step={step} setStep={setStep} />
</div>
<CartRecommended />
</div>

View File

@ -432,6 +432,13 @@ input#VinSearchInput {
line-height: 1.4em;
}
.heading-9-copy,
.text-block-21-copy {
width: 250px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@media (max-width: 767px) {
.w-layout-hflex.flex-block-6 {

View File

@ -2848,6 +2848,7 @@ body {
flex-flow: column;
flex: 1;
display: flex;
}
.text-field-copy {
@ -3114,6 +3115,7 @@ body {
}
.block-name {
max-width: 300px;
flex-flow: column;
flex: 1;
display: flex;
@ -3912,6 +3914,7 @@ body {
font-size: var(--_fonts---font-size--small-font-size);
height: 20px;
margin-top: 0;
max-width: 100%;
margin-bottom: 0;
font-weight: 700;
}
@ -4334,7 +4337,7 @@ body {
color: var(--_fonts---color--black);
font-size: var(--_fonts---font-size--bigger);
text-align: right;
max-width: 100px;
max-width: 200px;
margin-top: 0;
margin-bottom: 0;
font-weight: 700;
@ -5524,7 +5527,7 @@ body {
}
.flex-block-39-copy {
width: 200px;
width: 150px;
}
.cart-ditail {
@ -7336,7 +7339,7 @@ body {
}
.flex-block-39-copy {
width: 200px;
width: 150px;
}
.heading-9-copy-copy {