Styling refactor
This commit is contained in:
@@ -14,16 +14,10 @@ export default function ProductDetailPage() {
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!id) return;
|
||||
fetch(`${API_BASE}/api/v1/products/${id}`)
|
||||
.then((res) => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status} – ${res.statusText}`);
|
||||
}
|
||||
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status} – ${res.statusText}`);
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => setProduct(data))
|
||||
@@ -32,12 +26,12 @@ export default function ProductDetailPage() {
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<main className="pet-detail-page">
|
||||
<div className="pet-detail-container">
|
||||
<Link href="/products" className="pet-detail-back">← Back to Products</Link>
|
||||
<main className="min-h-screen py-12 px-8 pb-20">
|
||||
<div className="max-w-[860px] mx-auto">
|
||||
<Link href="/products" className="inline-block mb-8 text-[#e68672] no-underline text-base font-semibold transition-colors hover:text-[#d4705e]">← Back to Products</Link>
|
||||
|
||||
{loading && <p className="adopt-status-msg">Loading product details...</p>}
|
||||
{error && <p className="adopt-status-msg adopt-error">{error}</p>}
|
||||
{loading && <p className="text-center text-[#666] text-[1.1rem] py-12">Loading product details...</p>}
|
||||
{error && <p className="text-center text-[#c0392b] text-[1.1rem] py-12">{error}</p>}
|
||||
|
||||
{!loading && !error && product && (
|
||||
<ProductProfile
|
||||
|
||||
@@ -21,21 +21,12 @@ export default function ProductsPage() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
setCurrentPage(0);
|
||||
|
||||
fetchAllPages((page) => {
|
||||
const params = new URLSearchParams({
|
||||
page: String(page),
|
||||
size: String(PAGE_SIZE),
|
||||
sort: "prodId,asc",
|
||||
});
|
||||
if (query) {
|
||||
params.set("q", query);
|
||||
}
|
||||
const params = new URLSearchParams({ page: String(page), size: String(PAGE_SIZE), sort: "prodId,asc" });
|
||||
if (query) params.set("q", query);
|
||||
return `${API_BASE}/api/v1/products?${params}`;
|
||||
})
|
||||
.then((allProducts) => {
|
||||
setProducts(allProducts);
|
||||
})
|
||||
.then(setProducts)
|
||||
.catch((err) => setError(err.message))
|
||||
.finally(() => setLoading(false));
|
||||
}, [query]);
|
||||
@@ -52,27 +43,27 @@ export default function ProductsPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="products-page">
|
||||
<section className="products-hero">
|
||||
<h1 className="products-hero-title">Shop Our Products</h1>
|
||||
<p className="products-hero-subtitle">Everything your pet needs, all in one place</p>
|
||||
<div className="title-decoration"></div>
|
||||
<main className="min-h-screen">
|
||||
<section className="text-center py-16 px-8 bg-gradient-to-b from-[#f9f9f9] to-white">
|
||||
<h1 className="text-5xl font-bold text-[#333] mb-4 tracking-tight max-[768px]:text-3xl max-[480px]:text-[1.6rem]">Shop Our Products</h1>
|
||||
<p className="text-2xl font-light text-[#666] mb-8 max-[768px]:text-[1.2rem]">Everything your pet needs, all in one place</p>
|
||||
<div className="w-[100px] h-1 bg-[#e68672] mx-auto mt-8 rounded-sm"></div>
|
||||
</section>
|
||||
|
||||
<section className="adopt-controls">
|
||||
<div className="adopt-controls-row">
|
||||
<form className="adopt-search-form" onSubmit={handleSearch}>
|
||||
<section className="max-w-[1200px] mx-auto mb-6 px-8">
|
||||
<div className="flex items-center justify-between flex-wrap gap-4">
|
||||
<form className="flex gap-3 items-center" onSubmit={handleSearch}>
|
||||
<input
|
||||
className="adopt-search-input"
|
||||
className="flex-1 max-w-[400px] px-4 py-[0.6rem] border-2 border-[#ddd] rounded-md text-base outline-none transition-colors focus:border-[#e68672] font-[inherit]"
|
||||
type="text"
|
||||
placeholder="Search by name or category..."
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
<button className="adopt-search-btn" type="submit">Search</button>
|
||||
<button className="px-[1.4rem] py-[0.6rem] bg-[#e68672] text-white border-none rounded-md text-base cursor-pointer transition-colors hover:bg-[#d4705e] font-[inherit]" type="submit">Search</button>
|
||||
{query && (
|
||||
<button
|
||||
className="adopt-clear-btn"
|
||||
className="px-4 py-[0.6rem] bg-transparent text-[#666] border-2 border-[#ddd] rounded-md text-base cursor-pointer transition-all hover:border-[#aaa] hover:text-[#333] font-[inherit]"
|
||||
type="button"
|
||||
onClick={() => { setLoading(true); setError(null); setSearch(""); setQuery(""); }}
|
||||
>
|
||||
@@ -83,19 +74,15 @@ export default function ProductsPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="adopt-grid-section">
|
||||
{loading && <p className="adopt-status-msg">Loading products...</p>}
|
||||
|
||||
{error && (
|
||||
<p className="adopt-status-msg">Unable to load products, please try again later.</p>
|
||||
)}
|
||||
|
||||
<section className="max-w-[1200px] mx-auto px-8 pb-16">
|
||||
{loading && <p className="text-center text-[#666] text-[1.1rem] py-12">Loading products...</p>}
|
||||
{error && <p className="text-center text-[#666] text-[1.1rem] py-12">Unable to load products, please try again later.</p>}
|
||||
{!loading && !error && products.length === 0 && (
|
||||
<p className="adopt-status-msg">No products found.</p>
|
||||
<p className="text-center text-[#666] text-[1.1rem] py-12">No products found.</p>
|
||||
)}
|
||||
|
||||
{!loading && !error && products.length > 0 && (
|
||||
<div className="adopt-grid">
|
||||
<div className="grid grid-cols-4 gap-7 max-[1024px]:grid-cols-3 max-[480px]:grid-cols-2 max-[360px]:grid-cols-1">
|
||||
{displayedProducts.map((product) => (
|
||||
<ProductCard
|
||||
key={product.prodId}
|
||||
@@ -108,18 +95,19 @@ export default function ProductsPage() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && !error && totalPages > 1 && (
|
||||
<div className="pagination-controls">
|
||||
<div className="flex items-center justify-center gap-[0.4rem] py-6 px-4 flex-wrap mt-6">
|
||||
<button
|
||||
className="pagination-btn"
|
||||
className="border-none rounded-lg px-[0.9rem] py-2 text-[0.9rem] font-semibold cursor-pointer transition-colors bg-[#e8e8e8] text-[#333] hover:bg-[#d0d0d0] disabled:bg-[#f0f0f0] disabled:text-[#aaa] disabled:cursor-not-allowed"
|
||||
onClick={() => setCurrentPage((p) => Math.max(0, p - 1))}
|
||||
disabled={currentPage === 0}
|
||||
>
|
||||
← Prev
|
||||
</button>
|
||||
<span className="pagination-info">Page {currentPage + 1} of {totalPages}</span>
|
||||
<span className="text-[0.9rem] text-[#555] font-medium">Page {currentPage + 1} of {totalPages}</span>
|
||||
<button
|
||||
className="pagination-btn"
|
||||
className="border-none rounded-lg px-[0.9rem] py-2 text-[0.9rem] font-semibold cursor-pointer transition-colors bg-[#e8e8e8] text-[#333] hover:bg-[#d0d0d0] disabled:bg-[#f0f0f0] disabled:text-[#aaa] disabled:cursor-not-allowed"
|
||||
onClick={() => setCurrentPage((p) => Math.min(totalPages - 1, p + 1))}
|
||||
disabled={currentPage === totalPages - 1}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user