page.tsx•3.53 kB
'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
interface Product {
id: string;
name: string;
price: number;
description: string;
categoryId: string;
createdAt: string;
}
export default function ProductsPage() {
const [products, setProducts] = useState<Product[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const limit = 10;
useEffect(() => {
const fetchProducts = async () => {
try {
setLoading(true);
const res = await fetch(`/api/products?page=${page}&limit=${limit}`);
if (!res.ok) {
throw new Error('Failed to fetch products');
}
const data = await res.json();
setProducts(data.data.products);
setTotal(data.data.total);
} catch (err) {
setError('Error fetching products. Please try again.');
console.error(err);
} finally {
setLoading(false);
}
};
fetchProducts();
}, [page]);
const totalPages = Math.ceil(total / limit);
return (
<div>
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-bold">Products</h1>
<button className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition">
Add New Product
</button>
</div>
{loading ? (
<div className="flex justify-center my-12">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-600"></div>
</div>
) : error ? (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
{error}
</div>
) : (
<>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{products.map((product) => (
<div key={product.id} className="bg-white p-6 rounded-lg shadow-md">
<h2 className="text-xl font-semibold mb-2">{product.name}</h2>
<p className="text-green-600 font-medium">${product.price.toFixed(2)}</p>
<p className="text-gray-600 mt-2 text-sm line-clamp-2">{product.description}</p>
<div className="mt-4">
<Link href={`/products/${product.id}`} className="text-blue-600 hover:underline">
View Details
</Link>
</div>
</div>
))}
</div>
{totalPages > 1 && (
<div className="flex justify-center mt-8">
<nav className="flex items-center space-x-2">
<button
onClick={() => setPage(page > 1 ? page - 1 : 1)}
disabled={page === 1}
className="px-3 py-1 rounded border bg-gray-100 disabled:opacity-50"
>
Previous
</button>
<span className="px-3 py-1">
Page {page} of {totalPages}
</span>
<button
onClick={() => setPage(page < totalPages ? page + 1 : totalPages)}
disabled={page === totalPages}
className="px-3 py-1 rounded border bg-gray-100 disabled:opacity-50"
>
Next
</button>
</nav>
</div>
)}
</>
)}
</div>
);
}