Skip to main content
The product catalog is the heart of Villa Buena’s shopping experience, allowing customers to discover and explore products through powerful search and filtering capabilities.

Overview

The product catalog displays all available products with:
  • Grid-based product card layout
  • Category filtering
  • Real-time search
  • Price sorting
  • Pagination for large inventories

Product Display

Product Cards

Each product is displayed in a responsive card showing key information:

Product Information

  • Product image
  • Product title
  • Price (formatted to 2 decimals)
  • Quick action buttons

User Actions

  • View: Navigate to detailed product page
  • Add: Add product directly to cart

Implementation

Products are rendered using the ProductCard component:
src/components/productCard/ProductCard.jsx
const ProductCard = ({ product }) => {
  const addToCart = useCartStore((state) => state.addToCart);
  const showToast = useUIStore((state) => state.showToast);

  const handleAddToCart = () => {
    addToCart({
      id: product.id,
      title: product.title,
      price: product.price,
      thumbnail: product.thumbnail,
    });
    showToast("Added to cart");
  };

  return (
    <div className="col-md-4 col-lg-3 mb-4">
      <div className="product-card h-100">
        <img
          src={product.thumbnail}
          className="product-card-image"
          alt={product.title}
        />
        <div className="card-body">
          <h6 className="product-card-title">{product.title}</h6>
          <p className="product-card-price">
            ${Number(product.price).toFixed(2)}
          </p>
          <div className="product-card-actions">
            <Link to={`/product/${product.id}`}>
              View
            </Link>
            <button onClick={handleAddToCart}>Add</button>
          </div>
        </div>
      </div>
    </div>
  );
};

Search and Filter System

Search Functionality

Users can search products by title with real-time results:
1

Enter Search Term

Type in the search box to filter products by title (case-insensitive)
2

Debounced Search

Search uses 500ms debouncing to prevent excessive filtering during typing
3

Instant Results

Product list updates automatically showing matching items
src/pages/Home.jsx
const processedProducts = useMemo(() => {
  const products = productsData?.products || [];
  let filtered = [...products];

  // Search filter
  if (searchParam) {
    filtered = filtered.filter((p) =>
      p.title.toLowerCase().includes(searchParam.toLowerCase()),
    );
  }

  return filtered;
}, [productsData, searchParam]);

Category Filtering

Products can be filtered by category using chip-style buttons:
src/components/filters/Filters.jsx
const handleCategoryChange = (cat) => {
  setSearchParams((prevParams) => {
    const params = new URLSearchParams(prevParams);
    
    if (cat) params.set("category", cat);
    else params.delete("category");
    
    params.set("page", "1"); // Reset to first page
    return params;
  });
};
Category filters are synced with URL parameters, allowing users to share filtered views via link.

Sorting Options

Products can be sorted by price in ascending or descending order:
  • Price: Low → High - Budget-friendly items first
  • Price: High → Low - Premium items first
  • Default - Original order from API
src/pages/Home.jsx
if (sort === "price-asc") {
  filtered.sort((a, b) => a.price - b.price);
}

if (sort === "price-desc") {
  filtered.sort((a, b) => b.price - a.price);
}

Pagination

Page Management

The catalog displays 12 products per page with navigation controls:
src/pages/Home.jsx
const limit = 12;

const totalPages = Math.ceil(processedProducts.length / limit);

const paginatedProducts = processedProducts.slice(
  (page - 1) * limit,
  page * limit,
);

User Interface

Previous Button

Navigate to previous page (disabled on page 1)

Page Indicator

Shows current page and total pages

Next Button

Navigate to next page (disabled on last page)
Pagination state is preserved in URL parameters, allowing users to bookmark specific pages.

Product Detail View

Clicking “View” on a product card navigates to a detailed product page with:

Features

  • Image Gallery: Multiple product images with thumbnail navigation
  • Full Description: Complete product information
  • Rating Display: Star rating with review count
  • Discount Badges: Show percentage discounts if available
  • Action Buttons: Add to cart or buy now
src/pages/productDetail/ProductDetail.jsx
const renderStars = (rating) => {
  const fullStars = Math.floor(rating);
  const emptyStars = 5 - fullStars;

  return (
    <>
      {"★".repeat(fullStars)}
      {"☆".repeat(emptyStars)}
    </>
  );
};
The product detail page includes an interactive image gallery:
1

Thumbnail Selection

Click any thumbnail to change the main displayed image
2

Active Indicator

Currently selected thumbnail is highlighted with active styling
3

Fallback Handling

If no images array exists, defaults to product thumbnail
src/pages/productDetail/ProductDetail.jsx
const images = product.images?.length ? product.images : [product.thumbnail];

const mainImage = selectedImage && images.includes(selectedImage) 
  ? selectedImage 
  : images[0];

Loading States

The catalog implements skeleton loading screens while fetching products to improve perceived performance.
src/pages/Home.jsx
{isLoading ? (
  Array.from({ length: limit }).map((_, index) => (
    <SkeletonCard key={index} />
  ))
) : (
  paginatedProducts.map((product) => (
    <ProductCard key={product.id} product={product} />
  ))
)}

Error Handling

If products fail to load, users see an error message with retry option:
src/pages/Home.jsx
{isError && (
  <div className="alert text-center mb-4">
    <h5>⚠️ Error loading products</h5>
    <p>{error?.message || "Something went wrong."}</p>
    <button onClick={() => refetch()}>Try Again</button>
  </div>
)}

URL State Management

All catalog filters and pagination state are managed via URL parameters:
This approach enables:
  • Shareable filtered/sorted views
  • Browser back/forward navigation
  • Bookmarkable product searches
src/pages/Home.jsx
const [searchParams, setSearchParams] = useSearchParams();

const category = searchParams.get("category") || "";
const searchParam = searchParams.get("search") || "";
const sort = searchParams.get("sort") || "";
const page = Number(searchParams.get("page")) || 1;

Best Practices

Performance

Use useMemo for filtering and sorting to prevent unnecessary recalculations

User Feedback

Show toast notifications when products are added to cart

Empty States

Display helpful message when no products match search criteria

Accessibility

Include aria-labels and proper alt text for all images

Build docs developers (and LLMs) love