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 {
|
body {
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -8,14 +7,15 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: left;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
|
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
|
||||||
background-color: #ff6b6b78;
|
background-color: #ff6b6b78;
|
||||||
}
|
}
|
||||||
.filters {
|
.filters {
|
||||||
text-align: center;
|
|
||||||
|
text-align: right;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
background-color: #ff6b6b78;
|
background-color: #ff6b6b78;
|
||||||
|
@ -23,6 +23,7 @@ h1 {
|
||||||
|
|
||||||
.filters input,
|
.filters input,
|
||||||
.filters select {
|
.filters select {
|
||||||
|
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
@ -183,6 +184,32 @@ button:hover:not(:disabled) {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
|
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 */
|
/* Responsive Design */
|
||||||
@media (max-width: 600px) {
|
@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 BreedInfoPage from "./components/BreedInfoPage";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
import { debounce } from "lodash";
|
import { debounce } from "lodash";
|
||||||
|
const apikey ="live_8bnMZatSzkOR7eyT6o0iwDmXQuTxPI3deVWy0KY2ABxDkTMJdTYZ13S9lVFmrZRO"
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [cats, setCats] = useState([]);
|
const [cats, setCats] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
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 [favorites, setFavorites] = useState(JSON.parse(localStorage.getItem("favorites")) || []);
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
|
const [randomCat,setRandomCat]=useState(null);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchCats = async () => {
|
const fetchCats = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let queryParams = `?limit=20&page=${page}`;
|
let queryParams = `?limit=20&page=${page}`;
|
||||||
|
|
||||||
if (filters.breed) queryParams += `&breed_ids=${filters.breed}`;
|
if (filters.breed) queryParams += `&breed_ids=${filters.breed}`;
|
||||||
if (filters.hairLength) queryParams += `&hair_length=${filters.hairLength}`;
|
if (filters.hairLength) queryParams += `&hair_length=${filters.hairLength}`;
|
||||||
if (filters.origin) queryParams += `&origin=${filters.origin}`;
|
if (filters.origin) queryParams += `&origin=${filters.origin}`;
|
||||||
|
@ -26,32 +29,56 @@ const App = () => {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://api.thecatapi.com/v1/images/search${queryParams}`,
|
`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");
|
if (!response.ok) throw new Error("Failed to fetch data");
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setCats((prevCats) => [...prevCats, ...data]);
|
setCats((prevCats) => (page === 1 ? data : [...prevCats, ...data]));
|
||||||
} catch (err) {
|
}
|
||||||
|
catch (err) {
|
||||||
console.error(err.message);
|
console.error(err.message);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
fetchCats();
|
fetchCats();
|
||||||
}, [filters, page]);
|
}, [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(
|
const debouncedHandleFilterChange = useCallback(
|
||||||
debounce((updatedFilters) => setFilters(updatedFilters), 300),
|
debounce((updatedFilters) => {
|
||||||
|
setFilters(updatedFilters);
|
||||||
|
setPage(1);
|
||||||
|
}, 300),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFilterChange = (e) => {
|
const handleFilterChange = (e) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
debouncedHandleFilterChange({ ...filters, [name]: value });
|
debouncedHandleFilterChange({ ...filters, [name]: value });
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -63,12 +90,26 @@ const App = () => {
|
||||||
<div>
|
<div>
|
||||||
<h1>Cat Gallery</h1>
|
<h1>Cat Gallery</h1>
|
||||||
<Filters filters={filters} onFilterChange={handleFilterChange} />
|
<Filters filters={filters} onFilterChange={handleFilterChange} />
|
||||||
|
|
||||||
<Gallery
|
<Gallery
|
||||||
cats={cats}
|
cats={cats}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onLoadMore={() => setPage((prev) => prev + 1)}
|
onLoadMore={() => setPage((prev) => prev + 1)}
|
||||||
onAddFavorite={(cat) => setFavorites([...favorites, cat])}
|
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={favorites}
|
favorites={favorites}
|
||||||
onRemoveFavorite={(id) =>
|
onRemoveFavorite={(id) =>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||||
import { useParams, useNavigate } from "react-router-dom";
|
import { useParams, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
const API_URL = "https://api.thecatapi.com/v1";
|
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 BreedInfoPage = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const Filters = ({ filters, onFilterChange }) => {
|
const Filters = ({ filters, onFilterChange}) => {
|
||||||
return (
|
return (
|
||||||
<div className="filters">
|
<div className="filters">
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ const Gallery = ({ cats, loading, onLoadMore, onAddFavorite }) => {
|
||||||
500:3,
|
500:3,
|
||||||
};
|
};
|
||||||
const handleCatClick =(cat)=>{
|
const handleCatClick =(cat)=>{
|
||||||
|
console.log('id',cat)
|
||||||
if (cat.breeds && cat.breeds[0] && cat.breeds[0].id) {
|
if (cat.breeds && cat.breeds[0] && cat.breeds[0].id) {
|
||||||
navigate(`/breed/${cat.breeds[0].id}`);
|
navigate(`/breed/${cat.breeds[0].id}`);
|
||||||
} else {
|
} 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