All files / src/components ProductList.tsx

72% Statements 18/25
100% Branches 10/10
40% Functions 2/5
69.56% Lines 16/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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          12x 12x 12x 12x 12x   12x   4x   4x       3x   1x   4x       4x               12x 8x   7x                                                                             2x                                                                                                        
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { apiClient, type ProductResponse } from "../services/api";
 
export const ProductList: React.FC = () => {
  const [products, setProducts] = useState<ProductResponse[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [searchTerm, setSearchTerm] = useState("");
  const [categoryFilter, setCategoryFilter] = useState("");
 
  useEffect(() => {
    const fetchProducts = async () => {
      setLoading(true);
      try {
        const response = await apiClient.getProducts({
          name: searchTerm || undefined,
          category: categoryFilter || undefined,
        });
        setProducts(response.data.content);
      } catch (err: any) {
        setError(`Failed to fetch products: ${err.response?.data?.message}`);
      } finally {
        setLoading(false);
      }
    };
 
    fetchProducts();
  }, [searchTerm, categoryFilter]);
 
  const handleSearch = (e: React.FormEvent) => {
    e.preventDefault();
    // Search is handled by the useEffect dependency
  };
 
  if (loading) return <div>Loading...</div>;
  if (error) return <div data-testid="error-message">{error}</div>;
 
  return (
    <div data-testid="product-list-page">
      <div className="page-header">
        <h1>Products</h1>
        <Link to="/products/create" data-testid="create-product">
          <button>Create New Product</button>
        </Link>
      </div>
 
      {/* Search and Filter */}
      <div className="product-filters">
        <form onSubmit={handleSearch}>
          <input
            type="text"
            placeholder="Search products..."
            value={searchTerm}
            onChange={(e) => setSearchTerm(e.target.value)}
            data-testid="search-input"
          />
        </form>
 
        <select
          value={categoryFilter}
          onChange={(e) => setCategoryFilter(e.target.value)}
          data-testid="category-filter"
        >
          <option value="">All Categories</option>
          <option value="Electronics">Electronics</option>
          <option value="Books">Books</option>
          <option value="Clothing">Clothing</option>
        </select>
      </div>
 
      {/* Product List */}
      <div className="product-list" data-testid="product-list">
        {products.length === 0 ? (
          <p>No products found</p>
        ) : (
          products.map((product) => (
            <div
              key={product.id}
              className="product-item"
              data-testid={`product-${product.id}`}
            >
              <h3>{product.name}</h3>
              <p>Price: ${product.price}</p>
              <p>Quantity: {product.quantity}</p>
              <p>Category: {product.category}</p>
 
              <div className="product-actions">
                <Link
                  to={`/products/${product.id}`}
                  data-testid={`view-product-${product.id}`}
                >
                  <button>View</button>
                </Link>
                <Link
                  to={`/products/edit/${product.id}`}
                  data-testid={`edit-product-${product.id}`}
                >
                  <button>Edit</button>
                </Link>
                <button
                  onClick={async () => {
                    if (
                      window.confirm(
                        "Are you sure you want to delete this product?"
                      )
                    ) {
                      try {
                        await apiClient.deleteProduct(product.id);
                        setProducts(
                          products.filter((p) => p.id !== product.id)
                        );
                      } catch (error) {
                        alert("Failed to delete product");
                      }
                    }
                  }}
                  data-testid={`delete-product-${product.id}`}
                >
                  Delete
                </button>
              </div>
            </div>
          ))
        )}
      </div>
    </div>
  );
};