Spaces:
Running
Running
File size: 5,709 Bytes
b89a86e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
import { useState, useEffect } from "react";
import { useLocation } from "wouter";
import { useQuery } from "@tanstack/react-query";
import { productsApi, categoriesApi } from "@/lib/api";
import Header from "@/components/layout/header";
import ProductCard from "@/components/product/product-card";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { ArrowLeft } from "lucide-react";
import { LoadingSpinner } from "@/components/ui/spinner";
export default function SearchResults() {
const [, setLocation] = useLocation();
const [searchQuery, setSearchQuery] = useState("");
const [selectedCategory, setSelectedCategory] = useState<string>("");
// Get search parameters from URL
useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
const query = urlParams.get('q') || '';
const category = urlParams.get('category') || '';
setSearchQuery(query);
setSelectedCategory(category);
}, []);
// Update URL when search parameters change
useEffect(() => {
const params = new URLSearchParams();
if (searchQuery) params.set('q', searchQuery);
if (selectedCategory) params.set('category', selectedCategory);
const newUrl = `/search${params.toString() ? '?' + params.toString() : ''}`;
window.history.replaceState({}, '', newUrl);
}, [searchQuery, selectedCategory]);
const { data: categories = [], isLoading: categoriesLoading } = useQuery({
queryKey: ['/api/categories'],
queryFn: () => categoriesApi.getAll(),
});
const { data: searchResults = [], isLoading: searchLoading } = useQuery({
queryKey: ['/api/products', { search: searchQuery, category: selectedCategory }],
queryFn: () => productsApi.getAll({
...(searchQuery && { search: searchQuery }),
...(selectedCategory && { category: selectedCategory })
}),
enabled: !!(searchQuery || selectedCategory),
});
const getPageTitle = () => {
if (searchQuery && selectedCategory) {
const categoryName = categories.find((c: any) => c.id === selectedCategory)?.name || 'Category';
return `"${searchQuery}" in ${categoryName}`;
} else if (searchQuery) {
return `"${searchQuery}"`;
} else if (selectedCategory) {
const categoryName = categories.find((c: any) => c.id === selectedCategory)?.name || 'Category';
return categoryName;
}
return 'Search Results';
};
return (
<div className="min-h-screen page-gradient">
<Header searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8" data-testid="search-results-page">
{/* Back Navigation */}
<Button
variant="ghost"
onClick={() => setLocation('/')}
className="mb-6"
data-testid="back-to-home"
>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Home
</Button>
{/* Search Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2" data-testid="search-title">
Search Results for {getPageTitle()}
</h1>
{!searchLoading && (
<p className="text-gray-600" data-testid="results-count">
{searchResults.length} {searchResults.length === 1 ? 'product' : 'products'} found
</p>
)}
</div>
{/* Search Results */}
<section data-testid="search-results-section">
{searchLoading ? (
<div className="flex flex-col items-center">
<LoadingSpinner text="Searching products..." />
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 mt-6 w-full">
{[...Array(8)].map((_, i) => (
<div key={i} className="glass-card rounded-2xl p-4">
<Skeleton className="aspect-square w-full mb-4 rounded-xl" />
<Skeleton className="h-4 w-3/4 mb-2" />
<Skeleton className="h-4 w-1/2 mb-2" />
<Skeleton className="h-6 w-1/4" />
</div>
))}
</div>
</div>
) : searchResults.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4 sm:gap-5 md:gap-6" data-testid="search-product-grid">
{searchResults.map((product: any) => (
<ProductCard key={product.id} product={product} />
))}
</div>
) : (searchQuery || selectedCategory) ? (
<div className="text-center py-12" data-testid="no-results">
<h3 className="text-xl font-semibold text-gray-900 mb-2">No products found</h3>
<p className="text-gray-600 mb-6">
Try adjusting your search terms or browse all products
</p>
<Button
onClick={() => {
setSearchQuery('');
setSelectedCategory('');
setLocation('/');
}}
data-testid="browse-all-products"
>
Browse All Products
</Button>
</div>
) : (
<div className="text-center py-12" data-testid="search-prompt">
<h3 className="text-xl font-semibold text-gray-900 mb-2">Search for products</h3>
<p className="text-gray-600">
Use the search bar above or select a category to find products
</p>
</div>
)}
</section>
</div>
</div>
);
} |