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>
);
};
|