diff --git a/src/Breed-Search b/src/Breed-Search new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/Breed-Search @@ -0,0 +1 @@ + diff --git a/src/Dog-Breed-Search b/src/Dog-Breed-Search new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/Dog-Breed-Search @@ -0,0 +1 @@ + diff --git a/src/components/Api.js b/src/components/Api.js new file mode 100644 index 0000000..802b7c6 --- /dev/null +++ b/src/components/Api.js @@ -0,0 +1,7 @@ +const BASE_URL = 'https://dog.ceo/api'; + +export async function fetchImagesByBreed(breed) { + const response = await fetch(`${BASE_URL}/breed/${breed}/images/random/10`); + const data = await response.json(); + return data.message; +} \ No newline at end of file diff --git a/src/components/App.css b/src/components/App.css new file mode 100644 index 0000000..6eb629a --- /dev/null +++ b/src/components/App.css @@ -0,0 +1,179 @@ +body, h1, h2, p { + margin: 0; + padding: 0; +} + +/* Set a background color for the whole app and use a default font */ +body { + background-color: #f5f5f5; + font-family: Arial, sans-serif; +} + +h1{ + height: 121px; + height: 24px; +} + +/* Header styling */ +.App-header { + color: black; + text-align: center; + padding: 1rem 0; + margin-right: 31%; + margin-top: 2%; +} + +/* Main content area styling */ +.App-main { + max-width: 800px; + margin: 0 auto; + padding: 2rem; +} + +/* Styling for input and button elements */ +input{ + padding: 0.5rem 1rem; + border: none; + font-size: 20px; + width: 630px; + height: 36px; + margin-left: 10px; + margin-bottom: 20px; + +} +button { + padding: 0.5rem 1rem; + border: none; + font-size: 16px; + width: 105px; + height: 55px; +} + +/* Button styling */ +button { + background-color: #333; + color: white; + cursor: pointer; + transition: background-color 0.3s ease; +} + +/* Button hover effect */ +button:hover { + background-color: #555; +} + +/* Image gallery layout */ +.images { + display: flex; + justify-content: space-evenly,flex-start; + flex-wrap: wrap; + gap: 1rem; +} + +/* Styling for individual image containers */ +.image { + flex: 0 0 calc(20% - 1rem); + padding: .5rem; + text-align: center; + position: relative; + overflow: hidden; + height: 160px; + width: 160px; + + /* Add a light gray background color */ +} + +/* Position the favorite button at the bottom of the container */ +.image button { + position: absolute; + left: 75%; + bottom: 5%; + color: white; + height: 30px; + width: 30px; + cursor: pointer; + +} + +/* Ensure images cover the entire box */ +.image img { + width: 100%; + height: 100%; + object-fit: cover; +} + +/* Center align text in the header */ +.App-header h1 { + margin: 0; +} + +/* Media query for smaller screens */ +@media (max-width: 600px) { + .images { + flex-direction: column; + } + + .image { + flex: 0 0 100%; + } +} + +/* Favorites section styling */ +.favorites { + margin-top: 2rem; + margin-bottom: 2rem; +} + +/* Image gallery layout for favorites */ +.favorite-images { + display: flex; + justify-content: space-evenly,flex-start; + flex-wrap: wrap; + margin-top: 20px; +} + +.favorites .favorite-image img{ + border-radius: 3%; +} + +/* Styling for individual image containers for favorites */ +.favorites .image { + padding: 1rem; + border: 1px solid #ddd; + text-align: center; + background-color: white; + position: relative; + overflow: hidden; + background-color: #f9f9f9; + /* Add a light gray background color */ + +} + +.favorites .favorite-image button{ + position: relative; + right: 20%; + bottom: 5%; + height: 30px; + width: 30px; + +} + +/* Position the favorite button at the bottom of the container for favorites */ +.favorites .image button { + position: absolute; + bottom: 1rem; + left: 50%; + color: white; + border: none; + cursor: pointer; + +} + + +/* Ensure images cover the entire box for favorites */ +.favorites .image img { + width: 100%; + height: 100%; + object-fit: cover; + +} \ No newline at end of file diff --git a/src/components/App.js b/src/components/App.js new file mode 100644 index 0000000..40c8305 --- /dev/null +++ b/src/components/App.js @@ -0,0 +1,53 @@ +import React, { useState } from 'react'; +import './App.css'; +import SearchInput from './SearchInput'; +import ImageGallery from './ImageGalary'; +import Favorites from './Favourites'; +import { fetchImagesByBreed } from './Api'; + +function App() { + const [images, setImages] = useState([]); + const [favorites, setFavorites] = useState([]); + + const handleSearch = async (breed) => { + try { + const newImages = await fetchImagesByBreed(breed); + console.log('API response:', newImages); + + if (Array.isArray(newImages)) { + setImages(newImages); + } else { + console.error('Invalid API response:', newImages); + } + } catch (error) { + console.error('Error fetching images:', error); + } + }; + + const handleFavorite = (imageUrl) => { + if (!favorites.includes(imageUrl)) { + setFavorites([...favorites, imageUrl]); + } + }; + + const handleUnfavorite = (imageUrl) => { + const newFavorites = favorites.filter((url) => url !== imageUrl); + setFavorites(newFavorites); + }; + + return ( +
+
+

Dog Breeds

+
+
+ + +
+ +
+
+ ); +} + +export default App; \ No newline at end of file diff --git a/src/components/App.test.js b/src/components/App.test.js new file mode 100644 index 0000000..1f03afe --- /dev/null +++ b/src/components/App.test.js @@ -0,0 +1,8 @@ +import { render, screen } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', () => { + render(); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/src/components/Favourites.js b/src/components/Favourites.js new file mode 100644 index 0000000..7396984 --- /dev/null +++ b/src/components/Favourites.js @@ -0,0 +1,20 @@ +import React from 'react'; +import { IoIosHeart } from "react-icons/io"; + +function Favorites({ favorites, Unfavorite }) { + return ( +
+

Favorites

+
+ {favorites.map((imageUrl) => ( +
+ Favorite Dog + +
+ ))} +
+
+ ); +} + +export default Favorites; \ No newline at end of file diff --git a/src/components/ImageGalary.js b/src/components/ImageGalary.js new file mode 100644 index 0000000..6a642bf --- /dev/null +++ b/src/components/ImageGalary.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { IoIosHeartEmpty } from "react-icons/io"; + +function ImageGallery({ images, Favorite }) { + return ( +
+ {images.map((imageUrl) => ( +
+ Dog + +
+ ))} +
+ ); +} + +export default ImageGallery; \ No newline at end of file diff --git a/src/components/SearchInput.js b/src/components/SearchInput.js new file mode 100644 index 0000000..51acd9e --- /dev/null +++ b/src/components/SearchInput.js @@ -0,0 +1,23 @@ +import React, { useState } from 'react'; + +function SearchInput({ onSearch }) { + const [breed, setBreed] = useState(''); + + const handleSearch = () => { + onSearch(breed); + }; + + return ( +
+ setBreed(e.target.value)} + /> + +
+ ); +} + +export default SearchInput; \ No newline at end of file diff --git a/src/components/index.css b/src/components/index.css new file mode 100644 index 0000000..ec2585e --- /dev/null +++ b/src/components/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/src/components/index.js b/src/components/index.js new file mode 100644 index 0000000..d563c0f --- /dev/null +++ b/src/components/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/src/components/setupTests.js b/src/components/setupTests.js new file mode 100644 index 0000000..8f2609b --- /dev/null +++ b/src/components/setupTests.js @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom';