diff --git a/src/components/App.tsx b/src/components/App.tsx index e8c7d74..31470ce 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,21 +1,28 @@ import React, { FC } from 'react' import styled from '@emotion/styled' import Header from './Header' +import Favourites from './Favourites' +import Main from "./Main" const App: FC = () => { return (
- {/* Happy coding! */} +
+ ) } const Container = styled.div({ margin: '0 auto', - height: '100%', width: '560px', paddingTop: '60px', + paddingBottom:'20px', + "@media (max-width: 500px)": { + width:'100%', + padding:'60px 10px 0' + }, }) -export default App +export default App \ No newline at end of file diff --git a/src/components/Favouriates.tsx b/src/components/Favouriates.tsx new file mode 100644 index 0000000..e5e1cc2 --- /dev/null +++ b/src/components/Favouriates.tsx @@ -0,0 +1,61 @@ +import React from 'react' +import styled from '@emotion/styled' +import { RootStateOrAny, useDispatch, useSelector } from 'react-redux' +import Heart from './Heart' +import DogImage from './dogImage' +import { favoriteFunc } from '../redux/actions' + +export default function Favourites() { + const favlist: string[] = useSelector((state: RootStateOrAny) => state.favorites) + const dispatch = useDispatch() + + const removefavorite = (url: string) => { + dispatch(favoriteFunc(url)) + } + + return ( + <> + + + Favorites + + {favlist.length === 0 ? ( + No dogs in favorites. + ) : ( + + {favlist.map((item, index) => { + return ( + removefavorite(item)} icon={'redHeartIcon'} /> + ) + })} + + )} + + ) +} + +const HeadingContain = styled.div({ + display: 'flex', + marginTop: '48px', +}) + + + +const Heading = styled.h1({ + fontWeight: 'bold', + fontSize: '24px', + lineHeight: '33px', + marginLeft: '20px', +}) + +const Result = styled.div({ + margin: '44px auto 0', + width: '100%', + display: 'grid', + gap: '30px', + gridTemplateColumns: 'repeat(3, 1fr)', +}) + +const Msg = styled.div({ + marginTop:"20px" +}) diff --git a/src/components/Heart.tsx b/src/components/Heart.tsx index 2431e55..5bde811 100644 --- a/src/components/Heart.tsx +++ b/src/components/Heart.tsx @@ -1,20 +1,17 @@ -import React, { FC } from 'react' -import styled from '@emotion/styled' -import { icons } from '../assets' - interface Props { icon: string alt: string + onClick?:React.MouseEventHandler } -const Heart: FC = ({ icon, alt }) => { - return +const Heart: FC = ({ icon, alt,onClick }) => { + return } const HeartIcon = styled.img({ width: '17px', height: '15px', alignSelf: 'center', + cursor:"pointer" }) - export default Heart diff --git a/src/components/Inputbox.tsx b/src/components/Inputbox.tsx new file mode 100644 index 0000000..ca019a8 --- /dev/null +++ b/src/components/Inputbox.tsx @@ -0,0 +1,61 @@ +import styled from '@emotion/styled' +import React from 'react' +import { icons } from '../assets/icons' + +type PropsTypes ={ + onSubmit:React.FormEventHandler + onChange:React.ChangeEventHandler + value:string +} + +function Inputbox({onSubmit,value, onChange}:PropsTypes) { + return ( +
+ + +
+ ) +} +const Form = styled.form({ + margin: '48px auto', + width: '100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + borderRadius: '4px', + border: 'none', +}) + +const Input = styled.input({ + width: '100%', + display: 'block', + padding: '8px 17px', + background: '#F7F7F7', + border: 'none', + fontFamily: 'Nunito Sans', + fontStyle: 'normal', + fontWeight: 400, + fontSize: '16px', + lineHeight: '22px', + color: '#44484C', + outline: "none" +}) + +const Button = styled.button({ + alignSelf: 'stretch', + padding: '0 16px', + background: '#0794E3', + border: 'none', + color: '#FFFFFF', + borderRadius: '4px', + display: 'flex', + gap: '5px', + alignItems: 'center', + justifyContent: 'center', + cursor: "pointer" +}) + +export default Inputbox diff --git a/src/components/Main.tsx b/src/components/Main.tsx new file mode 100644 index 0000000..1dce5a7 --- /dev/null +++ b/src/components/Main.tsx @@ -0,0 +1,71 @@ +import React, { useState, FormEvent } from 'react' +import styled from '@emotion/styled' +import DogImage from './DogImage' +import { RootStateOrAny, useDispatch, useSelector } from 'react-redux' +import { favoriteFunc } from '../redux/actions' +import Inputbox from './Inputbox' + +type Maintypes = { + message?: string[] + status?: string +} + +function Main() { + const [search, Setsearch] = useState("") + const [doglist, Setdoglist] = useState([]) + const [err,Seterr]=useState(false) + const favlist: string[] = useSelector((state: RootStateOrAny) => state.favorites) + const dispatch = useDispatch() + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault() + const res: Maintypes = await (await fetch(`https://dog.ceo/api/breed/${search}/images/random/10`)).json() + if (res.status === "success") { + Setdoglist(res.message) + }else{ + Setdoglist([]) + Seterr(true) + } + } + + const Favorite = (url: string) => { + dispatch(favoriteFunc(url)) + } + + + return ( + <> + + Setsearch(e.target.value)}/> + {!doglist.length ? + <> + {err&&
No result found
} + : + <> + + {doglist.map((item, index) => { + return ( + Favorite(item)} icon={favlist.includes(item) ? 'redHeartIcon' : 'whiteHeartIcon'} /> + ) + })} + + + } + + ) +} + +const Result = styled.div({ + margin: '48px auto', + width: '100%', + display: 'grid', + gap: '30px', + gridTemplateColumns: 'repeat(3,1fr)', +}) + + +const Line = styled.div({ + borderBottom: '1px solid #DADADA', +}) + +export default Main diff --git a/src/components/dogImage.tsx b/src/components/dogImage.tsx new file mode 100644 index 0000000..0e12961 --- /dev/null +++ b/src/components/dogImage.tsx @@ -0,0 +1,39 @@ +import React from 'react' +import styled from '@emotion/styled' +import Heart from './Heart' + + +function DogImage({ src, icon, onClick }) { + return ( + + + + + + + ) +} + + +const Container = styled.div({ + position: "relative" +}) + +const Image = styled.img({ + width: "160px", + height: "160px", + objectFit: "cover", + borderRadius: "4px", + "@media (max-width: 600px)": { + width:'130px', + height:"130px" + }, +}) + +const Icon = styled.div({ + position: "absolute", + bottom: '10px', + right: '10px', +}) + +export default DogImage \ No newline at end of file diff --git a/src/redux/actions.ts b/src/redux/actions.ts index e69de29..6e39a93 100644 --- a/src/redux/actions.ts +++ b/src/redux/actions.ts @@ -0,0 +1,7 @@ +export const favoriteFunc =(url:string)=>{ + return { + type:"FAVORITE", + payload:url + } + } + \ No newline at end of file diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index be51d22..86455db 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -1,6 +1,22 @@ -export const reducer = (initialState = {}, action) => { +import { AnyAction } from 'redux' + +interface ReducerState { + favorites: string[] +} + +const initialState: ReducerState = { + favorites: [], +} + +export const favoriteReducer = (state = initialState, action: AnyAction) => { switch (action.type) { + case 'FAVORITE': + if (state.favorites.includes(action.payload)) { + return { favorites: [...state.favorites.filter((item) => item !== action.payload)] } + } else { + return { favorites: [...state.favorites, action.payload] } + } default: - return initialState + return state } -} +} \ No newline at end of file diff --git a/src/redux/store.ts b/src/redux/store.ts index 06536aa..da30d22 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -1,4 +1,8 @@ import { createStore } from 'redux' -import { reducer } from './reducer' -export default createStore(reducer) +import { favoriteReducer } from './reducer' + +const store = createStore(favoriteReducer) + + +export default store \ No newline at end of file