Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

Challenge completed #66

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"lint": "tslint './src/**/*.ts*' --format stylish --project . --force",
"start": "yarn run start-dev",
"start-dev": "webpack-dev-server --config=configs/webpack/dev.js",
"test": "jest --watch --coverage --config=configs/jest.config.js"
"test": "jest --watch --coverage --config=configs/jest.config.js",
"format": "prettier --write \"src/**/*.tsx\""
},
"devDependencies": {
"@babel/cli": "^7.6.4",
Expand All @@ -35,6 +36,7 @@
"jest-environment-enzyme": "^7.1.1",
"jest-enzyme": "^7.1.1",
"node-sass": "^4.13.1",
"prettier": "^3.0.3",
"react": "^16.10.2",
"react-dom": "^16.10.2",
"rimraf": "^3.0.0",
Expand All @@ -43,7 +45,9 @@
"style-loader": "^1.0.0",
"terser-webpack-plugin": "^5.1.1",
"ts-jest": "^26.5.4",
"tslint": "^5.20.0",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"tslint-plugin-prettier": "^2.3.0",
"typescript": "4.5.4",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.41.1",
Expand All @@ -56,6 +60,7 @@
"@emotion/core": "^10.0.22",
"@emotion/styled": "^10.0.22",
"@hot-loader/react-dom": "16.10.2",
"axios": "^1.5.0",
"react-hot-loader": "^4.12.15",
"react-redux": "^7.1.1",
"redux": "^4.0.4"
Expand Down
70 changes: 63 additions & 7 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,77 @@
import React, { FC } from 'react'
import React, { FC, useState, useEffect } from 'react'
import styled from '@emotion/styled'
import Header from './Header'
import SearchBar from './SearchBar'
import GridItem from './GridItem'
import Favorites from './Favorites'
import { useSelector } from 'react-redux'

const App: FC = () => {
const [breed, setBreed] = useState('terrier')
const [dogs, setDogs] = useState([])

// error handling hooks
const [dataComming, setDataComming] = useState(false)

// redux call
const getdata = useSelector((state) => state.searchreducer.carts)

// data updates a/c getData from search bar
useEffect(() => {
if (getdata.length > 0) {
const newBreed = getdata[0] // getdata is an array
fetchDogData(newBreed)
} else {
fetchDogData('affenpinscher')
}
}, [getdata])

const fetchDogData = async (currentBreed) => {
try {
const noOfData = 10
const res = await fetch(`https://dog.ceo/api/breed/${currentBreed}/images/random/${noOfData}`)
const data = await res.json()
setDogs(data.message)
setDataComming(true)
} catch (error) {
setDataComming(false)
}
}

return (
<Container>
<Header />
{/* Happy coding! */}
</Container>
<>
{dataComming === true ? (
<Container>
<Header />
<SearchBar />
<>
<GridItem dogs={dogs} />
</>

<Favorites />
<div></div>
</Container>
) : (
<NoData>
<h1>Loading ...</h1>
</NoData>
)}
</>
)
}

const Container = styled.div({
position: 'relative',
alignItems: 'center',
margin: '0 auto',
height: '100%',
width: '560px',
height: '168%',
width: '681.73px',
paddingTop: '60px',
})

const NoData = styled.div`
text-align: center;
margin-top: 50vh;
`

export default App
71 changes: 71 additions & 0 deletions src/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useState } from 'react'
import styled from '@emotion/styled'
import Heart from './Heart'
import { useDispatch } from 'react-redux'
import { ADD, DLT } from '../redux/actions'

const Card = ({ prop1, prop2, index }) => {
const dispatch = useDispatch()

// favorite prop1 or width size is 128px
let isTrue = false
if (prop1 === '128') {
isTrue = true
}
const [isFavorite, setIsFavorite] = useState(isTrue)
const send = (e) => {
dispatch(ADD(e))
}

const dlt = (id) => {
dispatch(DLT(id))
}

const toggleFavorite = () => {
// add toggle
if (isFavorite === false) {
send(prop2)
setIsFavorite(!isFavorite)
}

// delete toggle
if (isFavorite) {
dlt(prop2)
setIsFavorite(!isFavorite)
}
}
return (
<Griditem prop2={prop2} prop1={prop1}>
<div className="box">
<div onClick={toggleFavorite} className="heartIcon">
<Heart icon={isFavorite ? 'redHeartIcon' : 'whiteHeartIcon'} alt="red_hear_icon" />
</div>
</div>
</Griditem>
)
}

export default Card

const Griditem = styled.div`
.box {
background-color: rgba(255, 255, 255, 0.8);
height: ${(props) => props.prop1}px;
width: ${(props) => props.prop1}px;
border-radius: 5px;
border: none;
position: relative;
background-image: url('${(props) => props.prop2}');
background-size: cover;
background-repeat: no-repeat;
.heartIcon {
position: absolute;
bottom: 0px;
right: 5px;
box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.25);
}
&:hover {
border: solid 1px #edf3f0;
}
}
`
36 changes: 36 additions & 0 deletions src/components/Favorites.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react'
import styled from '@emotion/styled'
import Heart from './Heart'
import GridFavoriteItem from './GridFavoriteItem'

const Favorites = () => {
return (
<Container>
<Heading>
<Heart icon="redHeartIcon" alt="red heart icon" />
<Title>Favorites</Title>
</Heading>
<GridFavoriteItem />
</Container>
)
}

export default Favorites
const Container = styled.div`
width: 100%;
left: 62px;
position: absolute;
top: 1036px;
`
const Heading = styled.div`
display: flex;
`

const Title = styled.div`
font-family: 'Nunito Sans', Arial, Helvetica, sans-serif;
font-weight: 700;
font-size: 24px;
line-height: 16px;
margin-left: 25.27px;
color: #000000;
`
42 changes: 42 additions & 0 deletions src/components/GridFavoriteItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react'
import styled from '@emotion/styled'
import Card from './Card'
import { useSelector } from 'react-redux'

// Favorite dog breeds data
const GridFavoriteItem = () => {
// Redux code
const getdata = useSelector((state) => state.favreducer.carts)

// Create a Set to store unique images because there are duplicates
const uniqueURLs = new Set(getdata)
const uniqueData = Array.from(uniqueURLs)

// height
const propValue1 = '128'
return (
<Container>
<GridContainer>
{uniqueData.map((imageURL, index) => (
<Card key={imageURL} prop1={propValue1} prop2={imageURL} />
))}
</GridContainer>
</Container>
)
}

export default GridFavoriteItem

const Container = styled.div`
position: absolute;
width: 560px;
top: 56px;
margin-bottom: 50px;
`

const GridContainer = styled.div`
display: grid;
grid-template-columns: auto auto auto auto;
column-gap: 20px;
row-gap: 20px;
`
42 changes: 42 additions & 0 deletions src/components/GridItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react'
import styled from '@emotion/styled'
import Card from './Card'

// general dog breeds images
const GridItem = ({ dogs }) => {
const propValue1 = '160' // height
const first10Images = dogs.slice(0, 10)
return (
<Container>
<GridContainer>
{first10Images.map((element, index) => (
<Card key={index} prop1={propValue1} prop2={element} />
))}
</GridContainer>
<hr />
</Container>
)
}

export default GridItem

const Container = styled.div`
position: absolute;
width: 560px;
left: 60px;
top: 196px;

hr {
position: absolute;
top: 800px;
width: 560px;
border: solid 1px #dadada;
}
`

const GridContainer = styled.div`
display: grid;
grid-template-columns: auto auto auto;
column-gap: 40px;
row-gap: 40px;
`
36 changes: 26 additions & 10 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,36 @@ const Header: FC = () => {
return (
<Container>
<Title>Dog Breeds</Title>
<Heart icon="redHeartIcon" alt="red heart icon" />
<div className="icon">
<Heart icon="redHeartIcon" alt="red heart icon" />
</div>
</Container>
)
}

const Container = styled.div({
display: 'flex',
justifyContent: 'space-between',
})
const Container = styled.div`
display: flex;
width: 560px;
justify-content: space-between;
position: absolute;
top: 57px;
left: 61px;
.icon {
margin-top: 8px;
}
`

const Title = styled.h1({
fontWeight: 'bold',
fontSize: '24px',
lineHeight: '33px',
})
const Title = styled.div`
position: relative;
font-family: 'Nunito Sans', sans-serif;
width: 131px;
height: 24px;
font-size: 24px;
font-weight: 700;
line-height: 33px;
letter-spacing: 0px;
text-align: left;
margin-bottom: 8px;
`

export default Header
20 changes: 14 additions & 6 deletions src/components/Heart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ interface Props {
}

const Heart: FC<Props> = ({ icon, alt }) => {
return <HeartIcon src={icons[icon]} alt={alt} />
return (
<Container>
<HeartIcon src={icons[icon]} alt={alt} />
</Container>
)
}

const HeartIcon = styled.img({
width: '17px',
height: '15px',
alignSelf: 'center',
})
const Container = styled.div`
position: relative;
align-self: center;
`

const HeartIcon = styled.img`
width: 16.73px;
height: 15px;
`

export default Heart
Loading