diff --git a/client/src/Pages_css/discoverPage.css b/client/src/Pages_css/discoverPage.css index f24019f32..f06e7772d 100644 --- a/client/src/Pages_css/discoverPage.css +++ b/client/src/Pages_css/discoverPage.css @@ -8,7 +8,7 @@ text-align: center; font-size: 2rem; font-weight: 700; - margin: 32px 0 24px; + margin: 8px 0 24px; } .discover-status { @@ -91,6 +91,41 @@ overflow: hidden; } +.pagination { + display: flex; + align-items: center; + justify-content: center; + gap: 20px; + margin-top: 36px; +} + +.pagination-btn { + padding: 8px 20px; + border: 2px solid #111; + border-radius: 8px; + background-color: #fff; + font-size: 0.95rem; + font-family: inherit; + cursor: pointer; + transition: background-color 0.15s ease; +} + +.pagination-btn:hover:not(:disabled) { + background-color: #3f94b3; + color: #fff; + border-color: #3f94b3; +} + +.pagination-btn:disabled { + opacity: 0.35; + cursor: default; +} + +.pagination-info { + font-size: 1rem; + color: #2f3335; +} + @media (max-width: 640px) { .flower-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); diff --git a/client/src/components/NavBar.css b/client/src/components/NavBar.css index 8297be5f7..f368bbb76 100644 --- a/client/src/components/NavBar.css +++ b/client/src/components/NavBar.css @@ -7,7 +7,7 @@ background-color: rgba(255, 255, 255, 0.55); backdrop-filter: blur(6px); border-radius: 12px; - margin-bottom: 200px; + margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); } diff --git a/client/src/pages/discoverPage.jsx b/client/src/pages/discoverPage.jsx index b4007636e..5f32806a7 100644 --- a/client/src/pages/discoverPage.jsx +++ b/client/src/pages/discoverPage.jsx @@ -2,15 +2,19 @@ import { useEffect, useState } from 'react'; import { Link, useSearchParams } from 'react-router'; import '../Pages_css/discoverPage.css'; +const PER_PAGE = 30; + const DiscoverPage = () => { const [searchParams] = useSearchParams(); const [flowers, setFlowers] = useState([]); const [isLoading, setIsLoading] = useState(true); + const [currentPage, setCurrentPage] = useState(1); const searchQuery = searchParams.get('search') || ''; useEffect(() => { setIsLoading(true); + setCurrentPage(1); const url = searchQuery ? `/api/flowers?search=${encodeURIComponent(searchQuery)}` : '/api/flowers'; @@ -22,6 +26,9 @@ const DiscoverPage = () => { .finally(() => setIsLoading(false)); }, [searchQuery]); + const totalPages = Math.ceil(flowers.length / PER_PAGE); + const pageFlowers = flowers.slice((currentPage - 1) * PER_PAGE, currentPage * PER_PAGE); + return (

@@ -35,7 +42,7 @@ const DiscoverPage = () => { )}
- {flowers.map(flower => ( + {pageFlowers.map(flower => ( { ))}
+ + {totalPages > 1 && ( +
+ + + Page {currentPage} of {totalPages} + + +
+ )}

); }; diff --git a/client/src/pages/flowerSearch.jsx b/client/src/pages/flowerSearch.jsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/package.json b/package.json index 5aff1be64..af8074db0 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "start": "npm run reset && nodemon --require dotenv/config server/server.js", "server": "nodemon --require dotenv/config server/server.js", - "reset": "cd server/config && node --require dotenv/config reset.js" + "reset": "node --require dotenv/config server/config/reset.js" }, "repository": { "type": "git", diff --git a/server/services/plantAPI.js b/server/services/plantAPI.js index 04a8288cd..3383b1651 100644 --- a/server/services/plantAPI.js +++ b/server/services/plantAPI.js @@ -1,18 +1,23 @@ -// Fetches flowers from external API and maps them to your DB schema const API_BASE = process.env.PLANT_API_BASE; -export const fetchFlowersFromAPI = async (page = 1) => { - const res = await fetch(`${API_BASE}/search?page=${page}&type=flower`, { - headers: { Authorization: `Bearer ${process.env.PLANT_API_KEY}` }, - }); +const mapPlant = (plant) => ({ + name: plant.common_name ?? plant.scientific_name?.[0] ?? "Unknown", + description: null, + flower_family: plant.family ?? null, + flower_meaning: null, + image_url: plant.default_image?.regular_url ?? plant.default_image?.original_url ?? null, +}); - const json = await res.json(); +export const fetchFlowersFromAPI = async (maxPages = 10) => { + const allFlowers = []; - return json.results.map((plant) => ({ - name: plant.common_names?.[0] ?? plant.scientific_name, - description: null, - flower_family: plant.family_name ?? null, - flower_meaning: null, - image_url: plant.preferred_image_url ?? null, - })); + for (let page = 1; page <= maxPages; page++) { + const res = await fetch(`${API_BASE}/species-list?key=${process.env.PLANT_API_KEY}&page=${page}`); + const json = await res.json(); + const data = json.data ?? []; + allFlowers.push(...data.map(mapPlant)); + if (page >= (json.last_page ?? 1)) break; + } + + return allFlowers; };