163 lines
5.7 KiB
TypeScript
163 lines
5.7 KiB
TypeScript
'use client';
|
|
|
|
import { SearchIcon, SlidersHorizontal } from 'lucide-react';
|
|
import { useState } from 'react';
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardFooter,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from '@/components/ui/card';
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
import { Input } from '@/components/ui/input';
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@/components/ui/select';
|
|
import { Slider } from '@/components/ui/slider';
|
|
|
|
// Mock product data
|
|
const products = [
|
|
{ id: 1, name: 'Laptop', category: 'Electronics', price: 999.99 },
|
|
{ id: 2, name: 'Smartphone', category: 'Electronics', price: 699.99 },
|
|
{ id: 3, name: 'Headphones', category: 'Electronics', price: 199.99 },
|
|
{ id: 4, name: 'T-shirt', category: 'Clothing', price: 24.99 },
|
|
{ id: 5, name: 'Jeans', category: 'Clothing', price: 49.99 },
|
|
{ id: 6, name: 'Sneakers', category: 'Footwear', price: 89.99 },
|
|
{ id: 7, name: 'Watch', category: 'Accessories', price: 149.99 },
|
|
{ id: 8, name: 'Backpack', category: 'Accessories', price: 79.99 },
|
|
];
|
|
|
|
const categories = ['Electronics', 'Clothing', 'Footwear', 'Accessories'];
|
|
|
|
export function ProductListComponent() {
|
|
const [priceRange, setPriceRange] = useState([0, 1000]);
|
|
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [sortBy, setSortBy] = useState('name');
|
|
|
|
const handleCategoryChange = (category: string) => {
|
|
setSelectedCategories((prev) =>
|
|
prev.includes(category)
|
|
? prev.filter((c) => c !== category)
|
|
: [...prev, category],
|
|
);
|
|
};
|
|
|
|
const filteredProducts = products
|
|
.filter(
|
|
(product) =>
|
|
(selectedCategories.length === 0 ||
|
|
selectedCategories.includes(product.category)) &&
|
|
product.price >= priceRange[0] &&
|
|
product.price <= priceRange[1] &&
|
|
product.name.toLowerCase().includes(searchQuery.toLowerCase()),
|
|
)
|
|
.sort((a, b) => {
|
|
if (sortBy === 'price') return a.price - b.price;
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
|
|
return (
|
|
<div className='container mx-auto px-4 py-8'>
|
|
<div className='flex flex-col md:flex-row gap-8'>
|
|
{/* Sidebar with filters */}
|
|
<aside className='w-full md:w-1/4 space-y-6'>
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Filters</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className='space-y-6'>
|
|
<div>
|
|
<h3 className='text-lg font-semibold mb-2'>Categories</h3>
|
|
{categories.map((category) => (
|
|
<div key={category} className='flex items-center space-x-2'>
|
|
<Checkbox
|
|
id={category}
|
|
checked={selectedCategories.includes(category)}
|
|
onCheckedChange={() => handleCategoryChange(category)}
|
|
/>
|
|
<label
|
|
htmlFor={category}
|
|
className='text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
|
>
|
|
{category}
|
|
</label>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div>
|
|
<h3 className='text-lg font-semibold mb-2'>Price Range</h3>
|
|
<Slider
|
|
min={0}
|
|
max={1000}
|
|
step={10}
|
|
value={priceRange}
|
|
onValueChange={setPriceRange}
|
|
/>
|
|
<div className='flex justify-between mt-2'>
|
|
<span>${priceRange[0]}</span>
|
|
<span>${priceRange[1]}</span>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</aside>
|
|
|
|
{/* Main content */}
|
|
<main className='w-full md:w-3/4'>
|
|
<div className='flex flex-col sm:flex-row justify-between items-center mb-6 gap-4'>
|
|
<div className='relative w-full sm:w-auto'>
|
|
<SearchIcon className='absolute left-2 top-1/2 transform -translate-y-1/2 text-gray-400' />
|
|
<Input
|
|
type='search'
|
|
placeholder='Search products...'
|
|
className='pl-8'
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className='flex items-center space-x-2 w-full sm:w-auto'>
|
|
<Select value={sortBy} onValueChange={setSortBy}>
|
|
<SelectTrigger className='w-full sm:w-[180px]'>
|
|
<SelectValue placeholder='Sort by' />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value='name'>Name</SelectItem>
|
|
<SelectItem value='price'>Price</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<Button variant='outline' size='icon'>
|
|
<SlidersHorizontal className='h-4 w-4' />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6'>
|
|
{filteredProducts.map((product) => (
|
|
<Card key={product.id}>
|
|
<CardHeader>
|
|
<CardTitle>{product.name}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p>Category: {product.category}</p>
|
|
<p className='font-bold mt-2'>${product.price.toFixed(2)}</p>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<Button className='w-full'>Add to Cart</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|