Fix item loading
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import PetCard from "@/components/PetCard";
|
import PetCard from "@/components/PetCard";
|
||||||
|
import { fetchAllPages } from "@/lib/fetchAllPages";
|
||||||
|
|
||||||
const API_BASE = "";
|
const API_BASE = "";
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ export default function AdoptPage() {
|
|||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
|
|
||||||
const PAGE_SIZE = 200;
|
const PAGE_SIZE = 100;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch(`${API_BASE}/api/v1/health`)
|
fetch(`${API_BASE}/api/v1/health`)
|
||||||
@@ -22,16 +23,23 @@ export default function AdoptPage() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = new URLSearchParams({ page: 0, size: PAGE_SIZE, sort: "petId,asc" });
|
setLoading(true);
|
||||||
if (query) params.set("q", query);
|
setError(null);
|
||||||
|
|
||||||
fetch(`${API_BASE}/api/v1/pets?${params}`)
|
fetchAllPages((page) => {
|
||||||
.then((res) => {
|
const params = new URLSearchParams({
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status} – ${res.statusText}`);
|
page: String(page),
|
||||||
return res.json();
|
size: String(PAGE_SIZE),
|
||||||
})
|
sort: "petId,asc",
|
||||||
.then((data) => {
|
status: "Available",
|
||||||
setPets(data.content ?? []);
|
});
|
||||||
|
if (query) {
|
||||||
|
params.set("q", query);
|
||||||
|
}
|
||||||
|
return `${API_BASE}/api/v1/pets?${params}`;
|
||||||
|
})
|
||||||
|
.then((allPets) => {
|
||||||
|
setPets(allPets);
|
||||||
})
|
})
|
||||||
.catch((err) => setError(err.message))
|
.catch((err) => setError(err.message))
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
@@ -58,7 +66,7 @@ export default function AdoptPage() {
|
|||||||
<input
|
<input
|
||||||
className="adopt-search-input"
|
className="adopt-search-input"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search by name or species..."
|
placeholder="Search by name, species, or breed..."
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import ProductCard from "@/components/ProductCard";
|
import ProductCard from "@/components/ProductCard";
|
||||||
|
import { fetchAllPages } from "@/lib/fetchAllPages";
|
||||||
|
|
||||||
const API_BASE = "";
|
const API_BASE = "";
|
||||||
|
|
||||||
@@ -12,24 +13,25 @@ export default function ProductsPage() {
|
|||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
|
|
||||||
const PAGE_SIZE = 200;
|
const PAGE_SIZE = 100;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = new URLSearchParams({ page: 0, size: PAGE_SIZE, sort: "prodId,asc" });
|
setLoading(true);
|
||||||
if (query) {
|
setError(null);
|
||||||
params.set("q", query);
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(`${API_BASE}/api/v1/products?${params}`)
|
fetchAllPages((page) => {
|
||||||
.then((res) => {
|
const params = new URLSearchParams({
|
||||||
if (!res.ok) {
|
page: String(page),
|
||||||
throw new Error(`HTTP ${res.status} – ${res.statusText}`);
|
size: String(PAGE_SIZE),
|
||||||
}
|
sort: "prodId,asc",
|
||||||
|
});
|
||||||
return res.json();
|
if (query) {
|
||||||
})
|
params.set("q", query);
|
||||||
.then((data) => {
|
}
|
||||||
setProducts(data.content ?? []);
|
return `${API_BASE}/api/v1/products?${params}`;
|
||||||
|
})
|
||||||
|
.then((allProducts) => {
|
||||||
|
setProducts(allProducts);
|
||||||
})
|
})
|
||||||
.catch((err) => setError(err.message))
|
.catch((err) => setError(err.message))
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
|
|||||||
@@ -5,7 +5,15 @@ export default function PetCard({petId, petName, petSpecies, petStatus, imageUrl
|
|||||||
return (
|
return (
|
||||||
<Link href={`/adopt/${petId}`} className="pet-card">
|
<Link href={`/adopt/${petId}`} className="pet-card">
|
||||||
<div className="pet-card-image-wrapper">
|
<div className="pet-card-image-wrapper">
|
||||||
<img src={imageUrl || "/images/pet-placeholder.png"} alt={petName} className="pet-card-image" />
|
<img
|
||||||
|
src={imageUrl || "/images/pet-placeholder.png"}
|
||||||
|
alt={petName}
|
||||||
|
className="pet-card-image"
|
||||||
|
onError={(e) => {
|
||||||
|
e.currentTarget.onerror = null;
|
||||||
|
e.currentTarget.src = "/images/pet-placeholder.png";
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="pet-card-body">
|
<div className="pet-card-body">
|
||||||
<h3 className="pet-card-name">{petName}</h3>
|
<h3 className="pet-card-name">{petName}</h3>
|
||||||
|
|||||||
@@ -5,7 +5,15 @@ export default function PetProfile({ petId, petName, petSpecies, petBreed, petAg
|
|||||||
return (
|
return (
|
||||||
<div className="pet-detail-card">
|
<div className="pet-detail-card">
|
||||||
<div className="pet-detail-image-wrapper">
|
<div className="pet-detail-image-wrapper">
|
||||||
<img src={imageUrl || "/images/pet-placeholder.png"} alt={petName} className="pet-detail-image" />
|
<img
|
||||||
|
src={imageUrl || "/images/pet-placeholder.png"}
|
||||||
|
alt={petName}
|
||||||
|
className="pet-detail-image"
|
||||||
|
onError={(e) => {
|
||||||
|
e.currentTarget.onerror = null;
|
||||||
|
e.currentTarget.src = "/images/pet-placeholder.png";
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pet-detail-info">
|
<div className="pet-detail-info">
|
||||||
|
|||||||
@@ -4,7 +4,15 @@ export default function ProductCard({ prodId, prodName, categoryName, prodPrice,
|
|||||||
return (
|
return (
|
||||||
<Link href={`/products/${prodId}`} className="pet-card">
|
<Link href={`/products/${prodId}`} className="pet-card">
|
||||||
<div className="pet-card-image-wrapper">
|
<div className="pet-card-image-wrapper">
|
||||||
<img src={imageUrl || "/images/product-placeholder.png"} alt={prodName} className="pet-card-image" />
|
<img
|
||||||
|
src={imageUrl || "/images/pet-placeholder.png"}
|
||||||
|
alt={prodName}
|
||||||
|
className="pet-card-image"
|
||||||
|
onError={(e) => {
|
||||||
|
e.currentTarget.onerror = null;
|
||||||
|
e.currentTarget.src = "/images/pet-placeholder.png";
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="pet-card-body">
|
<div className="pet-card-body">
|
||||||
<h3 className="pet-card-name">{prodName}</h3>
|
<h3 className="pet-card-name">{prodName}</h3>
|
||||||
|
|||||||
@@ -4,7 +4,15 @@ export default function ProductProfile({ prodName, categoryName, prodDesc, prodP
|
|||||||
return (
|
return (
|
||||||
<div className="pet-detail-card">
|
<div className="pet-detail-card">
|
||||||
<div className="pet-detail-image-wrapper">
|
<div className="pet-detail-image-wrapper">
|
||||||
<img src={imageUrl || "/images/product-placeholder.png"} alt={prodName} className="pet-detail-image" />
|
<img
|
||||||
|
src={imageUrl || "/images/pet-placeholder.png"}
|
||||||
|
alt={prodName}
|
||||||
|
className="pet-detail-image"
|
||||||
|
onError={(e) => {
|
||||||
|
e.currentTarget.onerror = null;
|
||||||
|
e.currentTarget.src = "/images/pet-placeholder.png";
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pet-detail-info">
|
<div className="pet-detail-info">
|
||||||
|
|||||||
19
web/lib/fetchAllPages.js
Normal file
19
web/lib/fetchAllPages.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export async function fetchAllPages(urlBuilder) {
|
||||||
|
const items = [];
|
||||||
|
let page = 0;
|
||||||
|
let totalPages = 1;
|
||||||
|
|
||||||
|
while (page < totalPages) {
|
||||||
|
const res = await fetch(urlBuilder(page));
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`HTTP ${res.status} – ${res.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
items.push(...(data.content ?? []));
|
||||||
|
totalPages = Math.max(data.totalPages ?? 1, 1);
|
||||||
|
page += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user