update code
This commit is contained in:
parent
213e225797
commit
4a1b27f9c7
33
src/App.css
33
src/App.css
|
@ -1,4 +1,3 @@
|
|||
/* General Styles */
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
|
@ -8,14 +7,15 @@ body {
|
|||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 7px;
|
||||
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
|
||||
background-color: #ff6b6b78;
|
||||
}
|
||||
.filters {
|
||||
text-align: center;
|
||||
|
||||
text-align: right;
|
||||
margin: 0px;
|
||||
margin-bottom: 8px;
|
||||
background-color: #ff6b6b78;
|
||||
|
@ -23,6 +23,7 @@ h1 {
|
|||
|
||||
.filters input,
|
||||
.filters select {
|
||||
|
||||
margin: 0 10px;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
|
@ -183,6 +184,32 @@ button:hover:not(:disabled) {
|
|||
text-decoration: underline;
|
||||
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
|
||||
}
|
||||
.random-cat {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.random-cat-image {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.random-cat-button {
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
padding: 10px 20px;
|
||||
background-color: #ff6f61;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.random-cat-button:hover {
|
||||
background-color: #ff4f41;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 600px) {
|
||||
|
|
53
src/App.js
53
src/App.js
|
@ -6,19 +6,22 @@ import Favorites from "./components/Favorites";
|
|||
import BreedInfoPage from "./components/BreedInfoPage";
|
||||
import "./App.css";
|
||||
import { debounce } from "lodash";
|
||||
|
||||
const apikey ="live_8bnMZatSzkOR7eyT6o0iwDmXQuTxPI3deVWy0KY2ABxDkTMJdTYZ13S9lVFmrZRO"
|
||||
const App = () => {
|
||||
const [cats, setCats] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [filters, setFilters] = useState({ breed: "", hairLength: "" });
|
||||
const [filters, setFilters] = useState({ breed: "", hairLength: "" ,origin:"" });
|
||||
const [favorites, setFavorites] = useState(JSON.parse(localStorage.getItem("favorites")) || []);
|
||||
const [page, setPage] = useState(1);
|
||||
const [randomCat,setRandomCat]=useState(null);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCats = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
let queryParams = `?limit=20&page=${page}`;
|
||||
|
||||
if (filters.breed) queryParams += `&breed_ids=${filters.breed}`;
|
||||
if (filters.hairLength) queryParams += `&hair_length=${filters.hairLength}`;
|
||||
if (filters.origin) queryParams += `&origin=${filters.origin}`;
|
||||
|
@ -26,32 +29,56 @@ const App = () => {
|
|||
const response = await fetch(
|
||||
`https://api.thecatapi.com/v1/images/search${queryParams}`,
|
||||
{
|
||||
headers: { "x-api-key": "live_8bnMZatSzkOR7eyT6o0iwDmXQuTxPI3deVWy0KY2ABxDkTMJdTYZ13S9lVFmrZRO" },
|
||||
headers: { "x-api-key": apikey },
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) throw new Error("Failed to fetch data");
|
||||
|
||||
const data = await response.json();
|
||||
setCats((prevCats) => [...prevCats, ...data]);
|
||||
} catch (err) {
|
||||
setCats((prevCats) => (page === 1 ? data : [...prevCats, ...data]));
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
fetchCats();
|
||||
}, [filters, page]);
|
||||
|
||||
const fetchRandomCat = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://api.thecatapi.com/v1/images/search?limit=1`,
|
||||
{
|
||||
headers: { "x-api-key": apikey },
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) throw new Error("Failed to fetch random cat");
|
||||
|
||||
const data = await response.json();
|
||||
setRandomCat(data[0]);
|
||||
} catch (err) {
|
||||
console.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedHandleFilterChange = useCallback(
|
||||
debounce((updatedFilters) => setFilters(updatedFilters), 300),
|
||||
debounce((updatedFilters) => {
|
||||
setFilters(updatedFilters);
|
||||
setPage(1);
|
||||
}, 300),
|
||||
[]
|
||||
);
|
||||
|
||||
const handleFilterChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
debouncedHandleFilterChange({ ...filters, [name]: value });
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -63,12 +90,26 @@ const App = () => {
|
|||
<div>
|
||||
<h1>Cat Gallery</h1>
|
||||
<Filters filters={filters} onFilterChange={handleFilterChange} />
|
||||
|
||||
<Gallery
|
||||
cats={cats}
|
||||
loading={loading}
|
||||
onLoadMore={() => setPage((prev) => prev + 1)}
|
||||
onAddFavorite={(cat) => setFavorites([...favorites, cat])}
|
||||
/>
|
||||
<button className="random-cat-button" onClick={fetchRandomCat}>
|
||||
Show Me a Random Cat!
|
||||
</button>
|
||||
{randomCat && (
|
||||
<div className="random-cat">
|
||||
<h2>Random Cat</h2>
|
||||
<img
|
||||
src={randomCat.url}
|
||||
alt="Random Cat"
|
||||
className="random-cat-image"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Favorites
|
||||
favorites={favorites}
|
||||
onRemoveFavorite={(id) =>
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
|||
import { useParams, useNavigate } from "react-router-dom";
|
||||
|
||||
const API_URL = "https://api.thecatapi.com/v1";
|
||||
const API_KEY = "live_8bnMZatSzkOR7eyT6o0iwDmXQuTxPI3deVWy0KY2ABxDkTMJdTYZ13S9lVFmrZRO"; // Replace with your actual API key
|
||||
const API_KEY = "live_8bnMZatSzkOR7eyT6o0iwDmXQuTxPI3deVWy0KY2ABxDkTMJdTYZ13S9lVFmrZRO";
|
||||
|
||||
const BreedInfoPage = () => {
|
||||
const { id } = useParams();
|
||||
|
|
|
@ -12,6 +12,7 @@ const Gallery = ({ cats, loading, onLoadMore, onAddFavorite }) => {
|
|||
500:3,
|
||||
};
|
||||
const handleCatClick =(cat)=>{
|
||||
console.log('id',cat)
|
||||
if (cat.breeds && cat.breeds[0] && cat.breeds[0].id) {
|
||||
navigate(`/breed/${cat.breeds[0].id}`);
|
||||
} else {
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
const API_URL = "https://api.thecatapi.com/v1";
|
||||
const API_KEY = "live_8bnMZatSzkOR7eyT6o0iwDmXQuTxPI3deVWy0KY2ABxDkTMJdTYZ13S9lVFmrZRO";
|
||||
|
||||
/**
|
||||
* Fetches a list of cat images with optional pagination.
|
||||
* @param {number} limit - Number of images to fetch.
|
||||
* @param {number} page - Page number for pagination.
|
||||
* @returns {Promise<Array>} - A promise that resolves to an array of cat images.
|
||||
*/
|
||||
export const fetchCats = async (limit = 10, page = 1) => {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/images/search?limit=${limit}&page=${page}`, {
|
||||
headers: {
|
||||
"x-api-key": API_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch cats: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
} catch (error) {
|
||||
console.error("Error fetching cats:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches a list of cat breeds.
|
||||
* @returns {Promise<Array>} - A promise that resolves to an array of cat breeds.
|
||||
*/
|
||||
export const fetchBreeds = async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/breeds`, {
|
||||
headers: {
|
||||
"x-api-key": API_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch breeds: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
} catch (error) {
|
||||
console.error("Error fetching breeds:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue