├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── manifest.json └── index.html ├── src ├── img │ ├── star-full.png │ ├── star-empty.png │ ├── logo.svg │ ├── apple.svg │ ├── nintendo.svg │ ├── gamepad.svg │ ├── xbox.svg │ ├── playstation.svg │ └── steam.svg ├── reducers │ ├── index.js │ ├── detailReducer.js │ └── gameReducer.js ├── util.js ├── reportWebVitals.js ├── App.js ├── actions │ ├── detailAction.js │ └── gamesAction.js ├── animations.js ├── index.js ├── components │ ├── GlobalStyles.js │ ├── Game.js │ ├── Nav.js │ └── GameDetail.js ├── api.js └── pages │ └── Home.js ├── .gitignore ├── README.md └── package.json /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriticalflare/Ignite/main/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriticalflare/Ignite/main/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriticalflare/Ignite/main/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/img/star-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriticalflare/Ignite/main/src/img/star-full.png -------------------------------------------------------------------------------- /src/img/star-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriticalflare/Ignite/main/src/img/star-empty.png -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import gamesReducer from "./gameReducer"; 3 | import detailReducer from "./detailReducer"; 4 | 5 | const rootReducer = combineReducers({ 6 | games: gamesReducer, 7 | detail: detailReducer, 8 | }); 9 | 10 | export default rootReducer; 11 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | export const smallImage = (imagePath, size) => { 2 | if (imagePath === null) return imagePath; 3 | const image = imagePath.match(/media\/screenshots/) 4 | ? imagePath.replace( 5 | "media/screenshots", 6 | `media/resize/${size}/-/screenshots` 7 | ) 8 | : imagePath.replace("/media/games/", `/media/resize/${size}/-/games/`); 9 | return image; 10 | }; 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import GlobalStyles from "./components/GlobalStyles"; 3 | import Nav from "./components/Nav"; 4 | import Home from "./pages/Home"; 5 | import { Route } from "react-router-dom"; 6 | 7 | function App() { 8 | return ( 9 |
10 | 11 |
16 | ); 17 | } 18 | 19 | export default App; 20 | -------------------------------------------------------------------------------- /src/actions/detailAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { gameDetailsUrl, gameScreenshotUrl } from "../api"; 3 | 4 | export const loadDetail = (id) => async (dispatch) => { 5 | dispatch({ 6 | type: "LOADING_DETAIL", 7 | }); 8 | 9 | const details = await axios.get(gameDetailsUrl(id)); 10 | const screenshots = await axios.get(gameScreenshotUrl(id)); 11 | 12 | dispatch({ 13 | type: "GET_DETAIL", 14 | payload: { 15 | game: details.data, 16 | screen: screenshots.data, 17 | }, 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ignite website 2 | Made this following [Dev Ed](https://www.youtube.com/channel/UClb90NQQcskPUGDIXsQEz5Q)'s excellent [course](https://developedbyed.com/p/the-creative-react-and-redux-course), but making the site responsive and mobile friendly was kept as an exercise to viewer, so here's my stab at it :) 3 | 4 | ## Built With 🛠 5 | 6 | - [React](https://reactjs.org/) 7 | - [React-router-dom](https://www.npmjs.com/package/react-router-dom) 8 | - [Framer Motion](https://www.framer.com/motion/) 9 | - [Styled Components](https://styled-components.com/) 10 | - [Redux](https://redux.js.org/) 11 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/animations.js: -------------------------------------------------------------------------------- 1 | export const fadeIn = { 2 | hidden: { 3 | opacity: 0, 4 | }, 5 | show: { 6 | opacity: 1, 7 | transition: { 8 | duration: 0.75, 9 | }, 10 | }, 11 | exit: { 12 | opacity: 0, 13 | transition: { 14 | duration: 0.75, 15 | }, 16 | }, 17 | }; 18 | 19 | export const popup = { 20 | hidden: { 21 | opacity: 0, 22 | scale: 0.5, 23 | }, 24 | show: { 25 | opacity: 1, 26 | scale: 1, 27 | transition: { 28 | duration: 0.75, 29 | }, 30 | }, 31 | exit: { 32 | opacity: 0, 33 | transition: { 34 | duration: 0.75, 35 | }, 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /src/img/apple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/reducers/detailReducer.js: -------------------------------------------------------------------------------- 1 | const initialState = { 2 | game: { platforms: [] }, 3 | screen: { results: [] }, 4 | isLoading: true, 5 | }; 6 | 7 | const detailReducer = (state = initialState, action) => { 8 | switch (action.type) { 9 | case "GET_DETAIL": 10 | return { 11 | ...state, 12 | isLoading: false, 13 | game: action.payload.game, 14 | screen: action.payload.screen, 15 | }; 16 | case "LOADING_DETAIL": 17 | return { 18 | ...state, 19 | isLoading: true, 20 | }; 21 | default: 22 | return { 23 | ...state, 24 | }; 25 | } 26 | }; 27 | 28 | export default detailReducer; 29 | -------------------------------------------------------------------------------- /src/reducers/gameReducer.js: -------------------------------------------------------------------------------- 1 | const initState = { 2 | popular: [], 3 | newGames: [], 4 | upcoming: [], 5 | searched: [], 6 | }; 7 | 8 | const gamesReducer = (state = initState, action) => { 9 | switch (action.type) { 10 | case "FETCH_GAMES": 11 | return { 12 | ...state, 13 | popular: action.payload.popular, 14 | upcoming: action.payload.upcoming, 15 | newGames: action.payload.newGames, 16 | }; 17 | case "FETCH_SEARCHED": 18 | return { 19 | ...state, 20 | searched: action.payload.searched, 21 | }; 22 | case "CLEAR_SEARCHED": 23 | return { 24 | ...state, 25 | searched: [], 26 | }; 27 | default: 28 | return { ...state }; 29 | } 30 | }; 31 | 32 | export default gamesReducer; 33 | -------------------------------------------------------------------------------- /src/img/nintendo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/actions/gamesAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { 3 | popularGamesUrl, 4 | upcomingGamesUrl, 5 | newGamesUrl, 6 | searchGameUrl, 7 | } from "../api"; 8 | 9 | export const loadGames = () => async (dispatch) => { 10 | console.log(popularGamesUrl()); 11 | const popularData = await axios.get(popularGamesUrl()); 12 | const newGamesData = await axios.get(newGamesUrl()); 13 | const upcomingData = await axios.get(upcomingGamesUrl()); 14 | 15 | dispatch({ 16 | type: "FETCH_GAMES", 17 | payload: { 18 | popular: popularData.data.results, 19 | newGames: newGamesData.data.results, 20 | upcoming: upcomingData.data.results, 21 | }, 22 | }); 23 | }; 24 | 25 | export const fetchSearch = (game_name) => async (dispatch) => { 26 | const searchGames = await axios.get(searchGameUrl(game_name)); 27 | 28 | dispatch({ 29 | type: "FETCH_SEARCHED", 30 | payload: { 31 | searched: searchGames.data.results, 32 | }, 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | import reportWebVitals from "./reportWebVitals"; 5 | import { createStore, applyMiddleware, compose } from "redux"; 6 | import { Provider } from "react-redux"; 7 | import rootReducer from "./reducers/index"; 8 | import thunk from "redux-thunk"; 9 | import { BrowserRouter } from "react-router-dom"; 10 | 11 | const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 12 | 13 | const store = createStore(rootReducer, composeEnhancer(applyMiddleware(thunk))); 14 | 15 | ReactDOM.render( 16 | 17 | 18 | 19 | 20 | 21 | 22 | , 23 | document.getElementById("root") 24 | ); 25 | 26 | // If you want to start measuring performance in your app, pass a function 27 | // to log results (for example: reportWebVitals(console.log)) 28 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 29 | reportWebVitals(); 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ignite", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.6", 7 | "@testing-library/react": "^11.2.2", 8 | "@testing-library/user-event": "^12.6.0", 9 | "axios": "^0.21.0", 10 | "framer-motion": "^3.1.1", 11 | "react": "^17.0.1", 12 | "react-dom": "^17.0.1", 13 | "react-redux": "^7.2.2", 14 | "react-router-dom": "^5.2.0", 15 | "react-scripts": "4.0.1", 16 | "redux": "^4.0.5", 17 | "redux-thunk": "^2.3.0", 18 | "styled-components": "^5.2.1", 19 | "web-vitals": "^0.2.4" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "react-app", 30 | "react-app/jest" 31 | ] 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/components/GlobalStyles.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from "styled-components"; 2 | 3 | const GlobalStyles = createGlobalStyle` 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | } 9 | 10 | html { 11 | &::-webkit-scrollbar{ 12 | width: 0.5rem; 13 | } 14 | &::-webkit-scrollbar-thumb{ 15 | background-color: darkgrey 16 | } 17 | &::-webkit-scrollbar-track { 18 | background: white; 19 | } 20 | } 21 | 22 | body { 23 | font-family: 'Montserrat', sans-serif; 24 | width: 100%; 25 | } 26 | 27 | h2 { 28 | font-size: 3rem; 29 | font-family: 'Abril Fatface', cursive; 30 | font-weight: lighter; 31 | } 32 | 33 | h3 { 34 | font-size: 1.3rem; 35 | color: #333; 36 | padding: 1.5rem 0rem; 37 | } 38 | 39 | p { 40 | font-size: 1.2rem; 41 | line-height: 200%; 42 | color: #696969; 43 | } 44 | 45 | a { 46 | text-decoration: none; 47 | color: #333; 48 | } 49 | 50 | img { 51 | display:block; 52 | } 53 | input { 54 | font-family: 'Montserrat', sans-serif; 55 | } 56 | `; 57 | 58 | export default GlobalStyles; 59 | -------------------------------------------------------------------------------- /src/components/Game.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | import { motion } from "framer-motion"; 4 | import { useDispatch } from "react-redux"; 5 | import { loadDetail } from "../actions/detailAction"; 6 | import { Link } from "react-router-dom"; 7 | import { smallImage } from "../util"; 8 | import { popup } from "../animations"; 9 | 10 | const Game = ({ name, released, image, id }) => { 11 | const dispatch = useDispatch(); 12 | const stringPathId = id.toString(); 13 | 14 | const loadDetailHandler = () => { 15 | document.body.style.overflow = "hidden"; 16 | dispatch(loadDetail(id)); 17 | }; 18 | 19 | return ( 20 | 21 | 28 | {name} 29 |

{released}

30 | 35 |
36 | 37 | ); 38 | }; 39 | 40 | const StyledGame = styled(motion.div)` 41 | min-height: 30vh; 42 | box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.2); 43 | text-align: center; 44 | border-radius: 1rem; 45 | cursor: pointer; 46 | overflow: hidden; 47 | img { 48 | width: 100%; 49 | height: 40vh; 50 | object-fit: cover; 51 | } 52 | 53 | `; 54 | 55 | export default Game; 56 | -------------------------------------------------------------------------------- /src/img/gamepad.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/api.js: -------------------------------------------------------------------------------- 1 | // Base URL 2 | 3 | const base_url = "https://api.rawg.io/api/"; 4 | 5 | // Get current month 6 | const getMonth = () => { 7 | const month = new Date().getMonth() + 1; 8 | if (month < 10) { 9 | return `0${month}`; 10 | } else { 11 | return month; 12 | } 13 | }; 14 | 15 | // Get current day 16 | const getDay = () => { 17 | const day = new Date().getDay() + 1; 18 | if (day < 10) { 19 | return `0${day}`; 20 | } else { 21 | return day; 22 | } 23 | }; 24 | 25 | const currentYear = new Date().getFullYear(); 26 | const currentMonth = getMonth(); 27 | const currentDay = getDay(); 28 | const currentDate = `${currentYear}-${currentMonth}-${currentDay}`; 29 | const lastYear = `${currentYear - 1}-${currentMonth}-${currentDay}`; 30 | const nextYear = `${currentYear + 1}-${currentMonth}-${currentDay}`; 31 | 32 | // Popular games 33 | 34 | const popular_games = `games?dates=${lastYear},${currentDate}&ordering=-rating&page_size=10`; 35 | const upcoming_games = `games?dates=${currentDate},${nextYear}&ordering=-added&page_size=10`; 36 | const new_games = `games?dates=${lastYear},${currentDate}&ordering=-released&page_size=10`; 37 | console.log(new_games); 38 | 39 | export const popularGamesUrl = () => `${base_url}${popular_games}`; 40 | 41 | export const upcomingGamesUrl = () => `${base_url}${upcoming_games}`; 42 | 43 | export const newGamesUrl = () => `${base_url}${new_games}`; 44 | 45 | export const gameDetailsUrl = (game_id) => `${base_url}games/${game_id}`; 46 | 47 | export const gameScreenshotUrl = (game_id) => 48 | `${base_url}games/${game_id}/screenshots`; 49 | 50 | export const searchGameUrl = (game_name) => 51 | `${base_url}games?search=${game_name}&page_size=9`; 52 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 18 | 19 | 28 | React App 29 | 30 | 31 | 32 | 33 |
34 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/img/xbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/components/Nav.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import styled from "styled-components"; 3 | import { motion } from "framer-motion"; 4 | import logo from "../img/logo.svg"; 5 | import { useDispatch } from "react-redux"; 6 | import { fetchSearch } from "../actions/gamesAction"; 7 | import { fadeIn } from "../animations"; 8 | const Nav = () => { 9 | const dispatch = useDispatch(); 10 | const [textInput, setTextInput] = useState(""); 11 | const inputHandler = (e) => { 12 | setTextInput(e.target.value); 13 | }; 14 | const submitSearch = (e) => { 15 | e.preventDefault(); 16 | dispatch(fetchSearch(textInput)); 17 | setTextInput(""); 18 | }; 19 | const clearSearched = () => { 20 | dispatch({ 21 | type: "CLEAR_SEARCHED", 22 | }); 23 | }; 24 | 25 | return ( 26 | 27 | 28 | logo 29 |

Ignite

30 |
31 |
32 | 33 | 36 |
37 |
38 | ); 39 | }; 40 | 41 | const StyledNav = styled(motion.nav)` 42 | padding: 3rem 5rem; 43 | text-align: center; 44 | input { 45 | width: 30%; 46 | font-size: 1.5rem; 47 | padding: 0.5rem; 48 | border: none; 49 | margin-top: 1rem; 50 | box-shadow: 0px 0px 30px rgba(0, 0, 0, 0.2); 51 | font-weight: bold; 52 | } 53 | button { 54 | font-size: 1.5rem; 55 | border: none; 56 | padding: 0.5rem 2rem; 57 | cursor: pointer; 58 | background: #ff7676; 59 | color: white; 60 | } 61 | @media screen and (max-width: 800px) { 62 | padding: 0rem 1rem; 63 | input { 64 | width: 100%; 65 | } 66 | button { 67 | font-size: 1rem; 68 | margin-top: 0.5rem; 69 | } 70 | } 71 | `; 72 | const Logo = styled(motion.div)` 73 | display: flex; 74 | justify-content: center; 75 | padding: 1rem; 76 | cursor: pointer; 77 | img { 78 | height: 2rem; 79 | width: 2rem; 80 | } 81 | `; 82 | export default Nav; 83 | -------------------------------------------------------------------------------- /src/img/playstation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import { motion, AnimatePresence, AnimateSharedLayout } from "framer-motion"; 2 | import React, { useEffect } from "react"; 3 | import { useDispatch, useSelector } from "react-redux"; 4 | import { loadGames } from "../actions/gamesAction"; 5 | import styled from "styled-components"; 6 | import Game from "../components/Game"; 7 | import GameDetail from "../components/GameDetail"; 8 | import { useLocation } from "react-router-dom"; 9 | import { fadeIn } from "../animations"; 10 | 11 | const Home = () => { 12 | const location = useLocation(); 13 | 14 | const pathId = location.pathname.split("/")[2]; 15 | 16 | const dispatch = useDispatch(); 17 | 18 | useEffect(() => { 19 | dispatch(loadGames()); 20 | }, []); 21 | 22 | useEffect(() => { 23 | // to unlock scrolling when moving back from details page on mobile 24 | if (typeof pathId === "undefined") { 25 | document.body.style.overflow = "auto"; 26 | } 27 | }, [pathId]); 28 | 29 | const { popular, newGames, upcoming, searched } = useSelector( 30 | (state) => state.games 31 | ); 32 | 33 | return ( 34 | 35 | 36 | 37 | {pathId && } 38 | 39 | {searched.length ? ( 40 |
41 |

Searched Games

42 | 43 | {searched.map((game) => ( 44 | 51 | ))} 52 | 53 |
54 | ) : ( 55 | "" 56 | )} 57 |

Upcoming Games

58 | 59 | {upcoming.map((game) => ( 60 | 67 | ))} 68 | 69 |

Popular Games

70 | 71 | {popular.map((game) => ( 72 | 79 | ))} 80 | 81 |

New Games

82 | 83 | {newGames.map((game) => ( 84 | 91 | ))} 92 | 93 |
94 |
95 | ); 96 | }; 97 | 98 | const GameList = styled(motion.div)` 99 | padding: 0rem 5rem; 100 | h2 { 101 | padding: 5rem 0rem; 102 | } 103 | @media screen and (max-width: 800px) { 104 | padding: 0rem 1rem; 105 | h2 { 106 | font-size: 2rem; 107 | padding: 2rem 0rem; 108 | text-align: center; 109 | } 110 | } 111 | `; 112 | 113 | const Games = styled(motion.div)` 114 | min-height: 80vh; 115 | display: grid; 116 | grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); 117 | padding: 0rem 5rem; 118 | column-gap: 3rem; 119 | row-gap: 5rem; 120 | @media screen and (max-width: 800px) { 121 | display: flex; 122 | flex-direction: column; 123 | padding: 0rem 0rem; 124 | column-gap: 0rem; 125 | row-gap: 2rem; 126 | justify-content: space-evenly; 127 | } 128 | `; 129 | 130 | export default Home; 131 | -------------------------------------------------------------------------------- /src/components/GameDetail.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | import { motion } from "framer-motion"; 4 | import { useSelector } from "react-redux"; 5 | import { useHistory } from "react-router-dom"; 6 | import { smallImage } from "../util"; 7 | import playstation from "../img/playstation.svg"; 8 | import steam from "../img/steam.svg"; 9 | import xbox from "../img/xbox.svg"; 10 | import nintendo from "../img/nintendo.svg"; 11 | import apple from "../img/apple.svg"; 12 | import gamepad from "../img/gamepad.svg"; 13 | import starEmpty from "../img/star-empty.png"; 14 | import starFull from "../img/star-full.png"; 15 | 16 | const GameDetail = ({ pathId }) => { 17 | const { screen, game, isLoading } = useSelector((state) => state.detail); 18 | 19 | const history = useHistory(); 20 | 21 | const exitDetailHandler = (e) => { 22 | const element = e.target; 23 | if (element.classList.contains("shadow")) { 24 | document.body.style.overflow = "auto"; 25 | history.push("/"); 26 | } 27 | }; 28 | 29 | const getStars = () => { 30 | const stars = []; 31 | const rating = Math.floor(game.rating); 32 | for (let i = 1; i <= 5; i++) { 33 | if (i <= rating) { 34 | stars.push(star); 35 | } else { 36 | stars.push(star); 37 | } 38 | } 39 | return stars; 40 | }; 41 | 42 | const getPlatform = (platform) => { 43 | switch (platform) { 44 | case "PlayStation 4": 45 | return playstation; 46 | case "Xbox One": 47 | return xbox; 48 | case "PC": 49 | return steam; 50 | case "Nintendo Switch": 51 | return nintendo; 52 | case "iOS": 53 | return apple; 54 | default: 55 | return gamepad; 56 | } 57 | }; 58 | 59 | return ( 60 | <> 61 | {!isLoading && ( 62 | 63 | 64 | 65 |
66 | {game.name} 67 |

Rating: {game.rating}

68 | {getStars()} 69 |
70 | 71 |

Platforms

72 | 73 | {game.platforms.map((data) => ( 74 | 78 | ))} 79 | 80 |
81 |
82 | 83 | 88 | 89 | 90 |

{game.description_raw}

91 |
92 |
93 | {screen.results.map((screen) => ( 94 | game 99 | ))} 100 |
101 |
102 |
103 | )} 104 | 105 | ); 106 | }; 107 | 108 | const CardShadow = styled(motion.div)` 109 | width: 100%; 110 | min-height: 100vh; 111 | overflow-y: scroll; 112 | background: rgba(0, 0, 0, 0.5); 113 | position: fixed; 114 | z-index: 10; 115 | top: 0; 116 | left: 0; 117 | &::-webkit-scrollbar { 118 | width: 0.5rem; 119 | } 120 | &::-webkit-scrollbar-thumb { 121 | background-color: #ff7676; 122 | } 123 | &::-webkit-scrollbar-track { 124 | background: white; 125 | } 126 | `; 127 | 128 | const Detail = styled(motion.div)` 129 | width: 80%; 130 | border-radius: 1rem; 131 | padding: 2rem 5rem; 132 | background: white; 133 | position: absolute; 134 | left: 10%; 135 | color: black; 136 | img { 137 | width: 100%; 138 | } 139 | @media screen and (max-width: 800px) { 140 | width: 100%; 141 | padding: 1rem 1rem; 142 | left: 0%; 143 | border-radius: 0rem; 144 | } 145 | `; 146 | 147 | const Stats = styled(motion.div)` 148 | display: flex; 149 | align-items: center; 150 | justify-content: space-between; 151 | img { 152 | display: inline; 153 | width: 2rem; 154 | height: 2rem; 155 | } 156 | `; 157 | 158 | const Info = styled(motion.div)` 159 | text-align: center; 160 | `; 161 | 162 | const Platforms = styled(motion.div)` 163 | display: flex; 164 | justify-content: space-evenly; 165 | img { 166 | margin-left: 3rem; 167 | } 168 | @media screen and (max-width: 800px) { 169 | display: block; 170 | } 171 | `; 172 | 173 | const Media = styled(motion.div)` 174 | margin-top: 5rem; 175 | img { 176 | width: 100%; 177 | } 178 | `; 179 | 180 | const Description = styled(motion.div)` 181 | margin: 5rem 0rem; 182 | `; 183 | export default GameDetail; 184 | -------------------------------------------------------------------------------- /src/img/steam.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | --------------------------------------------------------------------------------