161 lines
6.3 KiB
TypeScript
161 lines
6.3 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useMemo } from 'react';
|
|
import { Product } from '@/types/types';
|
|
import { ProductCard } from './ProductCard';
|
|
import { SlidersHorizontal, X, Search } from 'lucide-react';
|
|
|
|
interface ProductsGridProps {
|
|
products: Product[];
|
|
}
|
|
|
|
export function ProductsGrid({ products }: ProductsGridProps) {
|
|
const [selectedBrands, setSelectedBrands] = useState<string[]>([]);
|
|
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
|
|
const [sortBy, setSortBy] = useState<string>('recommended');
|
|
const [showFiltersMobile, setShowFiltersMobile] = useState(false);
|
|
|
|
// Pobranie unikalnych wartości do filtrów
|
|
const brands = useMemo(() => Array.from(new Set(products.map(p => p.brand))), [products]);
|
|
|
|
const categoryLabels: Record<string, string> = {
|
|
'scienne': 'Ścienne',
|
|
'multisplit': 'Multisplit',
|
|
'kasetonowe': 'Kasetonowe',
|
|
'przenosne': 'Przenośne',
|
|
};
|
|
const categories = useMemo(() => Array.from(new Set(products.map(p => p.category))), [products]);
|
|
|
|
// Logika filtrowania i sortowania
|
|
const filteredProducts = useMemo(() => {
|
|
let result = [...products];
|
|
|
|
if (selectedBrands.length > 0) {
|
|
result = result.filter(p => selectedBrands.includes(p.brand));
|
|
}
|
|
if (selectedCategories.length > 0) {
|
|
result = result.filter(p => selectedCategories.includes(p.category));
|
|
}
|
|
|
|
if (sortBy === 'price-asc') {
|
|
result.sort((a, b) => a.price - b.price);
|
|
} else if (sortBy === 'price-desc') {
|
|
result.sort((a, b) => b.price - a.price);
|
|
} else if (sortBy === 'power-asc') {
|
|
result.sort((a, b) => a.power - b.power);
|
|
} else if (sortBy === 'power-desc') {
|
|
result.sort((a, b) => b.power - a.power);
|
|
}
|
|
|
|
return result;
|
|
}, [products, selectedBrands, selectedCategories, sortBy]);
|
|
|
|
const toggleBrand = (brand: string) => {
|
|
setSelectedBrands(prev =>
|
|
prev.includes(brand) ? prev.filter(b => b !== brand) : [...prev, brand]
|
|
);
|
|
};
|
|
|
|
const toggleCategory = (category: string) => {
|
|
setSelectedCategories(prev =>
|
|
prev.includes(category) ? prev.filter(c => c !== category) : [...prev, category]
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="flex flex-col md:flex-row gap-8 py-12">
|
|
{/* Mobile Filter Toggle */}
|
|
<div className="md:hidden flex justify-between items-center mb-4">
|
|
<span className="font-semibold">{filteredProducts.length} wyników</span>
|
|
<button className="btn btn-outline btn-primary bg-white" onClick={() => setShowFiltersMobile(!showFiltersMobile)}>
|
|
<SlidersHorizontal className="w-4 h-4 mr-2" />
|
|
Filtry
|
|
</button>
|
|
</div>
|
|
|
|
{/* Sidebar (Filtry) */}
|
|
<aside className={`w-full md:w-64 shrink-0 ${showFiltersMobile ? 'block' : 'hidden md:block'}`}>
|
|
<div className="bg-white p-6 rounded-2xl border border-border sticky top-24">
|
|
<h3 className="font-bold text-lg font-heading text-primary mb-6">Filtrowanie</h3>
|
|
|
|
<div className="space-y-6">
|
|
{/* Marki */}
|
|
<div>
|
|
<h4 className="font-semibold text-sm mb-3">Marka urządzenia</h4>
|
|
<div className="space-y-2">
|
|
{brands.map(brand => (
|
|
<label key={brand} className="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
type="checkbox"
|
|
checked={selectedBrands.includes(brand)}
|
|
onChange={() => toggleBrand(brand)}
|
|
className="rounded border-border text-primary focus:ring-primary"
|
|
/>
|
|
<span className="text-sm text-text-muted">{brand}</span>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Kategorie */}
|
|
<div>
|
|
<h4 className="font-semibold text-sm mb-3">Typ urządzenia</h4>
|
|
<div className="space-y-2">
|
|
{categories.map(category => (
|
|
<label key={category} className="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
type="checkbox"
|
|
checked={selectedCategories.includes(category)}
|
|
onChange={() => toggleCategory(category)}
|
|
className="rounded border-border text-primary focus:ring-primary"
|
|
/>
|
|
<span className="text-sm text-text-muted">{categoryLabels[category] || category}</span>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Główna sekcja (Narzędzia + Siatka) */}
|
|
<div className="flex-grow">
|
|
{/* Pasek narzędzi */}
|
|
<div className="hidden md:flex justify-between items-center bg-white p-4 rounded-xl border border-border mb-8">
|
|
<span className="text-text-muted font-medium">{filteredProducts.length} wyników</span>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<label htmlFor="sort" className="text-sm font-semibold text-primary">Sortuj według:</label>
|
|
<select
|
|
id="sort"
|
|
value={sortBy}
|
|
onChange={(e) => setSortBy(e.target.value)}
|
|
className="border border-border rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-primary/20 bg-slate-50"
|
|
>
|
|
<option value="recommended">Rekomendowane</option>
|
|
<option value="price-asc">Cena rosnąco</option>
|
|
<option value="price-desc">Cena malejąco</option>
|
|
<option value="power-asc">Moc rosnąco</option>
|
|
<option value="power-desc">Moc malejąco</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Siatka */}
|
|
{filteredProducts.length > 0 ? (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{filteredProducts.map(product => (
|
|
<ProductCard key={product.slug} product={product} />
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="text-center py-20 bg-slate-50 rounded-2xl border border-border">
|
|
<h3 className="text-xl font-bold text-primary mb-2">Brak wyników</h3>
|
|
<p className="text-text-muted">Spróbuj zmienić kryteria filtrowania.</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|