Files
template-ecommerce/src/pages/Search.tsx
T

138 lines
7.3 KiB
TypeScript

import { useState } from "react";
import { toast } from "sonner";
import { Link } from "react-router-dom";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Search as SearchIcon, Star, Heart, X, Sparkles } from "lucide-react";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
const allProducts = [
{ id: 1, name: "Classic Leather Jacket", price: 189, gradient: "from-rose-100 to-stone-200", rating: 5, category: "Outerwear" },
{ id: 2, name: "Silk Evening Dress", price: 245, gradient: "from-amber-100 to-orange-200", rating: 5, category: "Dresses" },
{ id: 3, name: "Cashmere Knit Sweater", price: 128, gradient: "from-stone-100 to-stone-300", rating: 4, category: "Knitwear" },
{ id: 4, name: "Linen Wide-Leg Trousers", price: 95, gradient: "from-rose-50 to-amber-100", rating: 4, category: "Bottoms" },
{ id: 5, name: "Italian Leather Handbag", price: 320, gradient: "from-stone-200 to-rose-100", rating: 5, category: "Accessories" },
{ id: 6, name: "Merino Wool Blazer", price: 165, gradient: "from-amber-50 to-stone-200", rating: 4, category: "Outerwear" },
{ id: 7, name: "Organic Cotton Tee", price: 45, gradient: "from-rose-100 to-rose-200", rating: 4, category: "Tops" },
{ id: 8, name: "Velvet Midi Skirt", price: 115, gradient: "from-stone-300 to-stone-100", rating: 5, category: "Bottoms" },
{ id: 9, name: "Suede Ankle Boots", price: 198, gradient: "from-amber-200 to-stone-200", rating: 5, category: "Footwear" },
{ id: 10, name: "Satin Blouse", price: 89, gradient: "from-rose-50 to-rose-100", rating: 4, category: "Tops" },
{ id: 11, name: "Tailored Wool Coat", price: 385, gradient: "from-stone-200 to-stone-400", rating: 5, category: "Outerwear" },
{ id: 12, name: "Pearl Drop Earrings", price: 68, gradient: "from-amber-100 to-rose-100", rating: 4, category: "Accessories" },
];
const filterChips = ["All", "Outerwear", "Dresses", "Tops", "Bottoms", "Accessories", "Footwear", "Knitwear"];
export default function SearchPage() {
const [query, setQuery] = useState("");
const [activeFilter, setActiveFilter] = useState("All");
const filtered = allProducts.filter((p) => {
const matchesQuery = query.length === 0 || p.name.toLowerCase().includes(query.toLowerCase());
const matchesFilter = activeFilter === "All" || p.category === activeFilter;
return matchesQuery && matchesFilter;
});
const renderStars = (count: number) =>
Array.from({ length: 5 }, (_, i) => (
<Star key={i} className={`h-3 w-3 \${i < count ? "fill-amber-400 text-amber-400" : "text-stone-300"}`} />
));
return (
<div className="min-h-screen bg-white text-stone-900 antialiased">
<Header />
<div className="mx-auto max-w-7xl px-6 pt-28 pb-20">
{/* Search Input */}
<div className="max-w-2xl mx-auto mb-10">
<div className="relative">
<SearchIcon className="absolute left-4 top-1/2 -translate-y-1/2 h-5 w-5 text-stone-400" />
<Input
type="text"
placeholder="Search for products..."
value={query}
onChange={(e) => setQuery(e.target.value)}
autoFocus
className="pl-12 pr-10 h-14 text-lg rounded-full border-stone-200 focus-visible:ring-rose-500 focus-visible:ring-2 focus-visible:border-rose-400 transition-shadow"
/>
{query && (
<button onClick={() => setQuery("")} className="absolute right-4 top-1/2 -translate-y-1/2 text-stone-400 hover:text-stone-600">
<X className="h-5 w-5" />
</button>
)}
</div>
</div>
{/* Results heading */}
{query && (
<h2 className="text-xl font-bold mb-6">
{filtered.length} result{filtered.length !== 1 ? "s" : ""} for "<span className="text-rose-600">{query}</span>"
</h2>
)}
{/* Filter Chips */}
<div className="flex flex-wrap gap-2 mb-8">
{filterChips.map((chip) => (
<Button
key={chip}
variant={activeFilter === chip ? "default" : "outline"}
size="sm"
className={`rounded-full text-sm \${activeFilter === chip ? "bg-stone-900" : ""}`}
onClick={() => setActiveFilter(chip)}
>
{chip}
</Button>
))}
</div>
{/* Results */}
{filtered.length > 0 ? (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 stagger">
{filtered.map((product) => (
<Link to="/product" key={product.id} className="group">
<Card className="overflow-hidden border-0 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-300">
<div className={`relative aspect-[3/4] bg-gradient-to-br \${product.gradient} overflow-hidden`}>
<div className="absolute inset-0" style={{ backgroundImage: "radial-gradient(circle, rgba(0,0,0,0.06) 1px, transparent 1px)", backgroundSize: "16px 16px" }} />
<div className="absolute top-1/4 -left-8 h-24 w-24 rounded-full bg-white/30 blur-2xl" />
<div className="absolute bottom-1/4 -right-8 h-20 w-20 rounded-full bg-white/20 blur-2xl" />
<div className="absolute inset-0 flex items-center justify-center text-stone-400 text-xs">Image</div>
<button className="absolute top-2 right-2 h-7 w-7 rounded-full bg-white/80 backdrop-blur-sm flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all hover:scale-110 active:scale-95">
<Heart className="h-3.5 w-3.5 text-stone-600" />
</button>
</div>
<CardContent className="p-3">
<h3 className="font-medium text-xs truncate">{product.name}</h3>
<div className="flex items-center gap-0.5 mt-1">{renderStars(product.rating)}</div>
<p className="mt-1.5 font-semibold text-sm">\${product.price}</p>
</CardContent>
</Card>
</Link>
))}
</div>
) : (
<div className="text-center py-20">
<div className="h-16 w-16 mx-auto rounded-full bg-stone-100 flex items-center justify-center mb-4">
<SearchIcon className="h-8 w-8 text-stone-400" />
</div>
<h3 className="text-xl font-bold mb-2">No results found</h3>
<p className="text-stone-500 mb-6 max-w-md mx-auto">
We couldn't find anything matching your search. Try different keywords or browse our categories.
</p>
<div className="flex flex-wrap justify-center gap-2">
<Badge variant="secondary" className="cursor-pointer hover:bg-stone-200">Summer Dresses</Badge>
<Badge variant="secondary" className="cursor-pointer hover:bg-stone-200">Leather Jackets</Badge>
<Badge variant="secondary" className="cursor-pointer hover:bg-stone-200">Cashmere</Badge>
<Badge variant="secondary" className="cursor-pointer hover:bg-stone-200">Accessories</Badge>
</div>
</div>
)}
</div>
<Footer />
</div>
);
}