From 882623a2bad2b4e43b4411617ca76f3ca0c67e7a Mon Sep 17 00:00:00 2001 From: deepakkumar9470 Date: Fri, 1 Sep 2023 11:39:27 +0530 Subject: [PATCH] dog breed challenge --- src/assets/icons/heart-icon.png | Bin 0 -> 248 bytes src/assets/icons/search-logo.png | Bin 0 -> 360 bytes src/assets/icons/search.png | Bin 0 -> 547 bytes src/components/App.tsx | 112 +++++++++++++++++++++++++++++-- src/components/Dog.tsx | 44 ++++++++++++ src/components/Dogs.tsx | 48 +++++++++++++ src/components/FavDogList.tsx | 65 ++++++++++++++++++ src/components/Header.tsx | 3 +- src/components/Loader.tsx | 26 +++++++ src/components/Search.tsx | 66 ++++++++++++++++++ yarn.lock | 8 +-- 11 files changed, 363 insertions(+), 9 deletions(-) create mode 100644 src/assets/icons/heart-icon.png create mode 100644 src/assets/icons/search-logo.png create mode 100644 src/assets/icons/search.png create mode 100644 src/components/Dog.tsx create mode 100644 src/components/Dogs.tsx create mode 100644 src/components/FavDogList.tsx create mode 100644 src/components/Loader.tsx create mode 100644 src/components/Search.tsx diff --git a/src/assets/icons/heart-icon.png b/src/assets/icons/heart-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a1395a474b99a1a79cafc4beaea9576eec0d1b3c GIT binary patch literal 248 zcmeAS@N?(olHy`uVBq!ia0vp^f``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBey^vCV@L(#(o5024F)_87n>M$e3*nLG_c4t zu;?skSir*hAVKJWW2Uobg>Lm1HP%V3pEvS4I31aG?2+VtcPZs;(X&3APg>PqFYvGX zahmJhs@)B2r89fvxFc1Um_PAqFz$J?V0A(YYwYFHnw61LvJV9GfBZPN@?E;h7gi-j pa{-Q@jQk>e77HpKuvRuQYehWzb$9iV0-(bfJYD@<);T3K0RT=DQMmvB literal 0 HcmV?d00001 diff --git a/src/assets/icons/search-logo.png b/src/assets/icons/search-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c46d7d98f9b56dc8d75e3ffdf48c72a84d073738 GIT binary patch literal 360 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6l5$8 za(7}_cTVOdki(Mh=8(36A4$1Iv z7BdK$u}(GMydx6AWRbwP>&dt3{wlK|;4SF0unR#)HDmIUh~PymYK%u@bW*tj*-jTP z%9Xlt&Yj71x^koH-$gu3_b*1z=Dcel;`;IDYk|F6m+p^z%T=~xxesGhnvaz8m*j%9 ziATBL1|=tza9zoN!(e{nj-r))omKWW#`O#VTYPT&@%gvA@lBp3y(YRr^sOMXoSl_J z_4MCqYzu;JOJA!xz1x6Y>&oUsV$iqb9yz=?{Z%9Jo$gmbAHeN99H??X)8Fs2Lq_%C*I;V zzAU%Eew@K>+`{oTuPnXgLm=+*DN)B5IOoI?K$@b#8z7< zA@CJXu{}kgj@KoMH+YV%DFTa%cgQ@t@FPXw4f-k&=##jT>qUT4wqS!AK53#M6X@pD1twpK&d5zCk?3x`esCB7i$ni#M1M&7?>mJH%6;FI$|*z%(w4_WCz^aUFN@ z3Wu<%tVAcqMQ=YM!nKqdM#ZvEh~(cB-;$R&SylpeiBIi&+|16e;toFIB6ehBNAN&w z@eP^G=_(|M|AM_3#zFM^eXG^QlW)wfE0<`7QOxE@9B-w>sU;GdMVD { + const [dogsdata, setDogsData] = useState([]) + const [selectedBreed, setSelectedBreed] = useState('') + const [dogImages, setDogImages] = useState([]) + const [searchQuery, setSearchQuery] = useState('') + const [favorites, setFavorites] = useState([]) + const [loading, setLoading] = useState(false) + const url = `https://dog.ceo/api/breed/hound/images` + + useEffect(() => { + const fetchDogs = async () => { + const res = await fetch(url) + const data = await res.json() + const breedList: string[] = await Object.keys(data.message) + setDogsData(breedList) + } + fetchDogs() + }, []) + + useEffect(() => { + if (selectedBreed) { + setLoading(true) + fetch(`https://dog.ceo/api/breed/${selectedBreed}/images`) + .then((response) => response.json()) + .then((data: BreedImage) => { + setDogImages(data.message.slice(0, 10)) + setSearchQuery('') + }) + .catch((error) => console.error(error)) + .finally(() => setLoading(false)) + } + }, [selectedBreed]) + + const handleDogSearch = () => { + if (searchQuery) { + setSelectedBreed(searchQuery) + } + } + + const addFavBreed = () => { + if (selectedBreed && !favorites.some((favorite) => favorite.breed === selectedBreed)) { + const breedImages = dogImages.map((image) => image) + setFavorites((prevFavorites) => [ + ...prevFavorites, + { breed: selectedBreed, images: breedImages }, + ]) + } + alert('Add to fav list..') + } + + const fetchFavoriteImages = async () => { + try { + const imageData = favorites.map((favourite) => + fetch(`https://dog.ceo/api/breed/${favourite}/images/random`) + .then((res) => res.json()) + .then((data) => data.message), + ) + + const favoriteImages = await Promise.all(imageData) + setDogImages(favoriteImages) + } catch (error) { + console.log(error) + } + } + + useEffect(() => { + fetchFavoriteImages() + }, [favorites]) + + const removeFromFavorites = (breed: string) => { + setFavorites((prevFavorites) => prevFavorites.filter((favorite) => favorite.breed !== breed)) + } + return (
- {/* Happy coding! */} + + +
+ ) } +const HR = styled.hr({ + width: '780px', + height: '2px', + color: '#DADADA', +}) + const Container = styled.div({ - margin: '0 auto', + padding: '20%', height: '100%', - width: '560px', + width: '600px', paddingTop: '60px', }) diff --git a/src/components/Dog.tsx b/src/components/Dog.tsx new file mode 100644 index 0000000..450ae36 --- /dev/null +++ b/src/components/Dog.tsx @@ -0,0 +1,44 @@ +import React from 'react' +import styled from '@emotion/styled' + +const Dog = ({ image, favorites, selectedBreed, addFavBreed, removeFromFavorites }) => { + return ( + + {`${selectedBreed}`} + + {favorites.includes(selectedBreed) || favorites.includes(image) ? ( + removeFromFavorites(selectedBreed)}>❤️ + ) : ( + 🤍 + )} + + ) +} + +const ImgContainer = styled.div({ + width: '250px', + height: '350px', + position: 'relative', +}) + +const Image = styled.img({ + width: '250px', + height: '250px', + borderRadius: '5px', + object: 'contain', + aspectRatio: '16/9', + position: 'relative', +}) + +const HeartIcon = styled.span({ + position: 'absolute', + bottom: '100px', + right: '10px', + fontSize: '20px', + color: 'red', + cursor: 'pointer', + border: 'none', + outline: 'none', +}) + +export default Dog diff --git a/src/components/Dogs.tsx b/src/components/Dogs.tsx new file mode 100644 index 0000000..3a4c37d --- /dev/null +++ b/src/components/Dogs.tsx @@ -0,0 +1,48 @@ +import React from 'react' +import Dog from './Dog' +import styled from '@emotion/styled' + +const Dogs = ({ + dogImages, + setSelectedBreed, + favorites, + selectedBreed, + removeFromFavorites, + addFavBreed, +}) => { + return ( + <> + {setSelectedBreed && ( + + {dogImages && Array.isArray(dogImages) + ? dogImages?.map((image, index) => ( + + )) + : null} + + )} + + ) +} + +const DogContainer = styled.div({ + display: 'grid', + gridTemplateColumns: 'repeat(3,1fr)', + gap: '20px', + paddingTop: '40px', +}) + +const Para = styled.p({ + width: '500px', + textAlign: 'center', + fontSize: '30px', +}) + +export default Dogs diff --git a/src/components/FavDogList.tsx b/src/components/FavDogList.tsx new file mode 100644 index 0000000..45e339d --- /dev/null +++ b/src/components/FavDogList.tsx @@ -0,0 +1,65 @@ +import React from 'react' +import styled from '@emotion/styled' +import Loader from './Loader' + +const FavDogList = ({ favorites, removeFromFavorites }) => { + return ( +
+ +

Favorites

+ {favorites.length > 0 ? ( + + {favorites.map((favorite) => ( + + {favorite.images.map((image) => ( +
+ + removeFromFavorites(favorite.breed)}>❤️ +
+ ))} +
+ ))} +
+ ) : ( + + )} +
+
+ ) +} + +const FavoritesContainer = styled.div({ + width: '780px', + marginTop: '50px', +}) + +const FavoriteImageContainer = styled.div({ + display: 'flex', + flexWrap: 'wrap', + gap: '10px', +}) + +const FavImgContainer = styled.div({ + display: 'flex', + flexWrap: 'wrap', + gap: '5px', +}) + +const FavoriteImage = styled.img({ + maxWidth: '150px', + maxHeight: '150px', + objectFit: 'cover', + borderRadius: '5px', + aspectRatio: '16/9', + position: 'relative', +}) +const FavIcon = styled.span({ + cursor: 'pointer', + marginleft: '5px', + color: 'red', + position: 'absolute', + right: '5px', + bottom: '5px', +}) + +export default FavDogList diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 275fbf8..856e89a 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -12,13 +12,14 @@ const Header: FC = () => { } const Container = styled.div({ + width: '780px', display: 'flex', justifyContent: 'space-between', }) const Title = styled.h1({ fontWeight: 'bold', - fontSize: '24px', + fontSize: '28px', lineHeight: '33px', }) diff --git a/src/components/Loader.tsx b/src/components/Loader.tsx new file mode 100644 index 0000000..4b3443a --- /dev/null +++ b/src/components/Loader.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import styled from '@emotion/styled' +const Loader = () => { + return +} + +const Spinner = styled.div` + border: 4px solid rgba(0, 0, 0, 0.1); + border-top: 4px solid #000; + border-radius: 50%; + width: 30px; + height: 30px; + animation: spin 1s linear infinite; + margin: 20px auto; + + @keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } +` + +export default Loader diff --git a/src/components/Search.tsx b/src/components/Search.tsx new file mode 100644 index 0000000..a723d3e --- /dev/null +++ b/src/components/Search.tsx @@ -0,0 +1,66 @@ +import styled from '@emotion/styled' +import React from 'react' +import searchIcon from '../assets/icons/search-logo.png' + +function Search({ searchQuery, setSearchQuery, handleDogSearch }) { + const handleSearch = (e) => { + setSearchQuery(e.target.value) + } + return ( + + + + + ) +} + +const SearchContainer = styled.div({ + width: '780px', + display: 'flex', + justifyContent: 'space-between', + marginTop: '20px', +}) + +const Input = styled.input({ + width: '100%', + background: '#F7F7F7', + height: '36px', + fontSize: '18px', + padding: '25px', + borderRadius: '4px', + border: 'none', + outline: 'none', +}) + +const Button = styled.button({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '200px', + background: '#0794E3', + fontSize: '18px', + color: '#fff', + height: '36px', + padding: '25px', + borderRadius: '4px', + border: 'none', + outline: 'none', + cursor: 'pointer', +}) + +const Img = styled.img({ + width: '25px', + height: '25px', + color: '#fff', + marginRight: '20px', +}) + +export default Search diff --git a/yarn.lock b/yarn.lock index 2b88693..7d2000a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10214,10 +10214,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.6.4: - version "3.9.9" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674" - integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w== +typescript@4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" + integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== uglify-js@3.4.x: version "3.4.10"