293 lines
12 KiB
TypeScript
293 lines
12 KiB
TypeScript
import React, { useState } from "react";
|
||
import { useRouter } from "next/router";
|
||
import Filters, { FilterConfig } from "./Filters";
|
||
import { useFavorites } from "@/contexts/FavoritesContext";
|
||
|
||
interface FavoriteListProps {
|
||
filters: FilterConfig[];
|
||
filterValues?: {[key: string]: any};
|
||
onFilterChange?: (type: string, value: any) => void;
|
||
searchQuery?: string;
|
||
onSearchChange?: (value: string) => void;
|
||
sortBy?: 'name' | 'brand' | 'date';
|
||
sortOrder?: 'asc' | 'desc';
|
||
onSortChange?: (sortBy: 'name' | 'brand' | 'date') => void;
|
||
onSortOrderChange?: (sortOrder: 'asc' | 'desc') => void;
|
||
}
|
||
|
||
const FavoriteList: React.FC<FavoriteListProps> = ({
|
||
filters,
|
||
filterValues = {},
|
||
onFilterChange,
|
||
searchQuery = '',
|
||
onSearchChange,
|
||
sortBy = 'date',
|
||
sortOrder = 'desc',
|
||
onSortChange,
|
||
onSortOrderChange
|
||
}) => {
|
||
const router = useRouter();
|
||
const { favorites, removeFromFavorites, clearFavorites } = useFavorites();
|
||
|
||
const handleRemove = (id: string) => {
|
||
removeFromFavorites(id);
|
||
};
|
||
|
||
const handleRemoveAll = () => {
|
||
clearFavorites();
|
||
};
|
||
|
||
// Функция для поиска товара
|
||
const handleSearchItem = (item: any) => {
|
||
// Формируем поисковый запрос из бренда и артикула
|
||
const searchQuery = `${item.brand} ${item.article}`;
|
||
// Переходим на страницу поиска с результатами
|
||
router.push(`/search-result?article=${encodeURIComponent(item.article)}&brand=${encodeURIComponent(item.brand)}`);
|
||
};
|
||
|
||
// Состояние для hover на иконке удаления всех
|
||
const [removeAllHover, setRemoveAllHover] = useState(false);
|
||
// Состояние для hover на корзине отдельного товара
|
||
const [hoveredId, setHoveredId] = useState<string | null>(null);
|
||
|
||
// Применяем фильтры к избранным товарам
|
||
const filteredFavorites = favorites.filter(item => {
|
||
// Фильтр по поисковому запросу
|
||
if (searchQuery) {
|
||
const query = searchQuery.toLowerCase();
|
||
const matchesSearch =
|
||
item.name.toLowerCase().includes(query) ||
|
||
item.brand.toLowerCase().includes(query) ||
|
||
item.article.toLowerCase().includes(query);
|
||
|
||
if (!matchesSearch) return false;
|
||
}
|
||
|
||
// Фильтр по производителю
|
||
const selectedBrands = filterValues['Производитель'] || [];
|
||
if (selectedBrands.length > 0 && !selectedBrands.includes(item.brand)) {
|
||
return false;
|
||
}
|
||
|
||
// Фильтр по цене
|
||
const priceRange = filterValues['Цена (₽)'];
|
||
if (priceRange && item.price) {
|
||
const [minPrice, maxPrice] = priceRange;
|
||
if (item.price < minPrice || item.price > maxPrice) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
});
|
||
|
||
// Применяем сортировку
|
||
const sortedFavorites = [...filteredFavorites].sort((a, b) => {
|
||
let comparison = 0;
|
||
|
||
switch (sortBy) {
|
||
case 'name':
|
||
comparison = a.name.localeCompare(b.name);
|
||
break;
|
||
case 'brand':
|
||
comparison = a.brand.localeCompare(b.brand);
|
||
break;
|
||
case 'date':
|
||
comparison = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
||
break;
|
||
default:
|
||
comparison = 0;
|
||
}
|
||
|
||
return sortOrder === 'asc' ? comparison : -comparison;
|
||
});
|
||
|
||
const handleSortClick = (newSortBy: 'name' | 'brand' | 'date') => {
|
||
if (sortBy === newSortBy) {
|
||
// Если тот же столбец, меняем порядок
|
||
onSortOrderChange?.(sortOrder === 'asc' ? 'desc' : 'asc');
|
||
} else {
|
||
// Если новый столбец, устанавливаем его и порядок по умолчанию
|
||
onSortChange?.(newSortBy);
|
||
onSortOrderChange?.(newSortBy === 'date' ? 'desc' : 'asc');
|
||
}
|
||
};
|
||
|
||
// SVG-галочки для сортировки — всегда видны у всех колонок
|
||
const getSortIcon = (columnSort: 'name' | 'brand' | 'date') => {
|
||
const isActive = sortBy === columnSort;
|
||
const isAsc = sortOrder === 'asc';
|
||
const color = isActive ? 'var(--_button---primary)' : '#94a3b8';
|
||
return (
|
||
<svg width="14" height="14" viewBox="0 0 20 20" fill="none" style={{marginLeft: 2}}>
|
||
<path
|
||
d={isActive ? (isAsc ? 'M6 12l4-4 4 4' : 'M6 8l4 4 4-4') : 'M6 8l4 4 4-4'}
|
||
stroke={color}
|
||
strokeWidth="2"
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
/>
|
||
</svg>
|
||
);
|
||
};
|
||
|
||
return (
|
||
<div className="w-layout-hflex core-product-card">
|
||
<Filters
|
||
filters={filters}
|
||
onFilterChange={onFilterChange || (() => {})}
|
||
filterValues={filterValues}
|
||
searchQuery={searchQuery}
|
||
onSearchChange={onSearchChange || (() => {})}
|
||
/>
|
||
<div className="w-layout-vflex flex-block-48">
|
||
<div className="w-layout-vflex product-list-cart">
|
||
{/* Информация о результатах фильтрации */}
|
||
{(searchQuery || Object.values(filterValues).some(v => Array.isArray(v) ? v.length > 0 : v)) && (
|
||
<div className="mb-4 p-3 bg-blue-50 rounded-lg">
|
||
<div className="text-sm text-blue-700">
|
||
Найдено {sortedFavorites.length} из {favorites.length} товаров
|
||
{searchQuery && (
|
||
<span> по запросу "{searchQuery}"</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="w-layout-hflex heading-list">
|
||
<div className="w-layout-hflex flex-block-61">
|
||
<div
|
||
className="sort-item-brand cursor-pointer hover:text-blue-600 flex items-center gap-1"
|
||
onClick={() => handleSortClick('brand')}
|
||
>
|
||
Производитель {getSortIcon('brand')}
|
||
</div>
|
||
<div className="sort-item-brand-copy">Артикул</div>
|
||
<div
|
||
className="sort-item-name cursor-pointer hover:text-blue-600 flex items-center gap-1"
|
||
onClick={() => handleSortClick('name')}
|
||
>
|
||
Наименование {getSortIcon('name')}
|
||
</div>
|
||
<div className="sort-item-comments">Комментарий</div>
|
||
</div>
|
||
{/* <div className="w-layout-hflex add-to-cart-block-copy">
|
||
<div className="text-sm font-medium text-gray-600">Действия</div>
|
||
</div> */}
|
||
{favorites.length > 0 && (
|
||
<div
|
||
className="w-layout-hflex select-all-block"
|
||
onClick={handleRemoveAll}
|
||
style={{ cursor: 'pointer', color: removeAllHover ? '#ec1c24' : undefined, transition: 'color 0.2s' }}
|
||
onMouseEnter={() => setRemoveAllHover(true)}
|
||
onMouseLeave={() => setRemoveAllHover(false)}
|
||
>
|
||
<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={removeAllHover ? "#ec1c24" : "#D0D0D0"}
|
||
style={{ transition: 'fill 0.2s' }}
|
||
/>
|
||
</svg>
|
||
</div>
|
||
)}
|
||
</div>
|
||
{sortedFavorites.map((item) => {
|
||
return (
|
||
<div className="div-block-21" key={item.id}>
|
||
<div className="w-layout-hflex favorite-item">
|
||
<div className="w-layout-hflex info-block-search">
|
||
<div className="w-layout-hflex block-detail">
|
||
<h4 className="brandname">{item.brand}</h4>
|
||
<h4 className="brandname">{item.article}</h4>
|
||
<div className="productname_f">{item.name}</div>
|
||
</div>
|
||
<div className="comments_f w-form">
|
||
<form className="form-copy">
|
||
<input
|
||
className="text-field-copy w-input"
|
||
maxLength={256}
|
||
name="Search-5"
|
||
data-name="Search 5"
|
||
placeholder="Комментарий"
|
||
type="text"
|
||
id={`Search-5-${item.id}`}
|
||
/>
|
||
</form>
|
||
<div className="success-message w-form-done">
|
||
<div>Thank you! Your submission has been received!</div>
|
||
</div>
|
||
<div className="error-message w-form-fail">
|
||
<div>Oops! Something went wrong while submitting the form.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="w-layout-hflex add-to-cart-block-copy">
|
||
<button
|
||
onClick={() => handleSearchItem(item)}
|
||
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors duration-200 flex items-center gap-2 text-sm font-medium"
|
||
style={{color: '#fff'}}
|
||
>
|
||
|
||
<svg
|
||
width="16"
|
||
height="16"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
>
|
||
<circle cx="11" cy="11" r="8" stroke="currentColor" strokeWidth="2"/>
|
||
<path d="m21 21-4.35-4.35" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||
</svg>
|
||
Поиск
|
||
</button>
|
||
<div className="w-layout-hflex control-element-copy">
|
||
{/* Корзина с hover-эффектом для удаления товара */}
|
||
<span
|
||
style={{ display: 'inline-flex' }}
|
||
onMouseEnter={() => setHoveredId(item.id)}
|
||
onMouseLeave={() => setHoveredId(null)}
|
||
>
|
||
<svg
|
||
width="18"
|
||
height="19"
|
||
viewBox="0 0 18 19"
|
||
fill="none"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
className="image-13"
|
||
style={{ cursor: 'pointer', transition: 'fill 0.2s' }}
|
||
onClick={() => handleRemove(item.id)}
|
||
>
|
||
<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={hoveredId === item.id ? "#ec1c24" : "#D0D0D0"}
|
||
style={{ transition: 'fill 0.2s' }}
|
||
/>
|
||
</svg>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
{sortedFavorites.length === 0 && (
|
||
<div style={{ padding: 32, textAlign: 'center', color: '#888' }}>
|
||
{favorites.length === 0 ? 'Нет избранных товаров' : 'Нет товаров, соответствующих фильтрам'}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default FavoriteList;
|