From b0f14e260a8523e84e4dd844fa66aa2e19847ef1 Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 3 Jun 2026 21:50:11 +0000 Subject: [PATCH] Switch to version 3: modified src/hooks/useProductCatalog.ts --- src/hooks/useProductCatalog.ts | 139 ++++++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 29 deletions(-) diff --git a/src/hooks/useProductCatalog.ts b/src/hooks/useProductCatalog.ts index b6a5877..c4ae5cf 100644 --- a/src/hooks/useProductCatalog.ts +++ b/src/hooks/useProductCatalog.ts @@ -1,34 +1,115 @@ -import { useState, useEffect } from 'react'; -import { fetchProducts } from '@/lib/api/product'; +"use client"; -interface UseProductCatalogProps { - initialProducts?: Product[]; - category?: string; - limit?: number; +import { useState, useMemo, useCallback } from "react"; +import { useRouter } from "next/navigation"; +import { useProducts } from "./useProducts"; +import type { Product } from "@/lib/api/product"; +import type { CatalogProduct } from "@/components/ecommerce/productCatalog/ProductCatalogItem"; +import type { ProductVariant } from "@/components/ecommerce/productDetail/ProductDetailCard"; + +export type SortOption = "Newest" | "Price: Low-High" | "Price: High-Low"; + +interface UseProductCatalogOptions { + basePath?: string; } -export const useProductCatalog = ({ initialProducts, category, limit }: UseProductCatalogProps) => { - const [products, setProducts] = useState(initialProducts || []); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); +export function useProductCatalog(options: UseProductCatalogOptions = {}) { + const { basePath = "/shop" } = options; + const router = useRouter(); + const { products: fetchedProducts, isLoading } = useProducts(); - useEffect(() => { - const loadProducts = async () => { - setLoading(true); - setError(null); - try { - const fetchedProducts = await fetchProducts({ category, limit }); - setProducts(fetchedProducts); - } catch (err) { - setError('Failed to fetch products'); - } - setLoading(false); + const [search, setSearch] = useState(""); + const [category, setCategory] = useState("All"); + const [sort, setSort] = useState("Newest"); + + const handleProductClick = useCallback((productId: string) => { + router.push(`${basePath}/${productId}`); + }, [router, basePath]); + + const catalogProducts: CatalogProduct[] = useMemo(() => { + if (fetchedProducts.length === 0) return []; + + return fetchedProducts.map((product) => ({ + id: product.id, + name: product.name, + price: product.price, + imageSrc: product.imageSrc, + imageAlt: product.imageAlt || product.name, + rating: product.rating || 0, + reviewCount: product.reviewCount, + category: product.brand, + onProductClick: () => handleProductClick(product.id), + })); + }, [fetchedProducts, handleProductClick]); + + const categories = useMemo(() => { + const categorySet = new Set(); + catalogProducts.forEach((product) => { + if (product.category) { + categorySet.add(product.category); + } + }); + return Array.from(categorySet).sort(); + }, [catalogProducts]); + + const filteredProducts = useMemo(() => { + let result = catalogProducts; + + if (search) { + const q = search.toLowerCase(); + result = result.filter( + (p) => + p.name.toLowerCase().includes(q) || + (p.category?.toLowerCase().includes(q) ?? false) + ); + } + + if (category !== "All") { + result = result.filter((p) => p.category === category); + } + + if (sort === "Price: Low-High") { + result = [...result].sort( + (a, b) => + parseFloat(a.price.replace("$", "").replace(",", "")) - + parseFloat(b.price.replace("$", "").replace(",", "")) + ); + } else if (sort === "Price: High-Low") { + result = [...result].sort( + (a, b) => + parseFloat(b.price.replace("$", "").replace(",", "")) - + parseFloat(a.price.replace("$", "").replace(",", "")) + ); + } + + return result; + }, [catalogProducts, search, category, sort]); + + const filters: ProductVariant[] = useMemo(() => [ + { + label: "Category", + options: ["All", ...categories], + selected: category, + onChange: setCategory, + }, + { + label: "Sort", + options: ["Newest", "Price: Low-High", "Price: High-Low"] as SortOption[], + selected: sort, + onChange: (value) => setSort(value as SortOption), + }, + ], [categories, category, sort]); + + return { + products: filteredProducts, + isLoading, + search, + setSearch, + category, + setCategory, + sort, + setSort, + filters, + categories, }; - - if (!initialProducts || initialProducts.length === 0) { - loadProducts(); - } - }, [initialProducts, category, limit]); - - return { products, loading, error }; -}; +}