Files
protekauto-frontend/src/components/FavoriteList.tsx
2025-06-29 12:36:49 +03:00

293 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;