├── .env ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── manifest.json └── index.html ├── src ├── assets │ ├── icons │ │ ├── gym.png │ │ ├── target.png │ │ ├── body-part.png │ │ ├── equipment.png │ │ ├── left-arrow.png │ │ └── right-arrow.png │ └── images │ │ ├── Logo.png │ │ ├── Logo-1.png │ │ └── banner.png ├── components │ ├── Loader.js │ ├── Footer.js │ ├── ExerciseCard.js │ ├── BodyPart.js │ ├── Navbar.js │ ├── SimilarExercises.js │ ├── HeroBanner.js │ ├── HorizontalScrollbar.js │ ├── ExerciseVideos.js │ ├── Detail.js │ ├── Exercises.js │ └── SearchExercises.js ├── index.js ├── utils │ └── fetchData.js ├── pages │ ├── Home.js │ └── ExerciseDetail.js ├── app.js └── App.css ├── .gitignore ├── package.json ├── .github └── workflows │ └── jekyll-gh-pages.yml ├── README.md └── .eslintrc.js /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_RAPID_API_KEY = aedff815f2mshff4e5da66ca3a11p1d4ce8jsna3fd75571a23 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/assets/icons/gym.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/src/assets/icons/gym.png -------------------------------------------------------------------------------- /src/assets/images/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/src/assets/images/Logo.png -------------------------------------------------------------------------------- /src/assets/icons/target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/src/assets/icons/target.png -------------------------------------------------------------------------------- /src/assets/images/Logo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/src/assets/images/Logo-1.png -------------------------------------------------------------------------------- /src/assets/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/src/assets/images/banner.png -------------------------------------------------------------------------------- /src/assets/icons/body-part.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/src/assets/icons/body-part.png -------------------------------------------------------------------------------- /src/assets/icons/equipment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/src/assets/icons/equipment.png -------------------------------------------------------------------------------- /src/assets/icons/left-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/src/assets/icons/left-arrow.png -------------------------------------------------------------------------------- /src/assets/icons/right-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nawabsahab16/gym-website/HEAD/src/assets/icons/right-arrow.png -------------------------------------------------------------------------------- /src/components/Loader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Stack } from '@mui/material'; 3 | import { InfinitySpin } from 'react-loader-spinner'; 4 | 5 | const Loader = () => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default Loader; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | 9 | root.render( 10 | 11 | 12 | 13 | 14 | , 15 | ); 16 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, Stack, Typography } from '@mui/material'; 3 | import Logo from '../assets/images/Logo-1.png'; 4 | 5 | const Footer = () => ( 6 | 7 | 8 | logo 9 | 10 | Made with ❤️ by Sameer Sharma 11 | 12 | ); 13 | 14 | export default Footer; -------------------------------------------------------------------------------- /src/utils/fetchData.js: -------------------------------------------------------------------------------- 1 | export const exerciseOptions = { 2 | method: 'GET', 3 | headers: { 4 | 'X-RapidAPI-Host': 'exercisedb.p.rapidapi.com', 5 | 'X-RapidAPI-Key': process.env.REACT_APP_RAPID_API_KEY, 6 | }, 7 | }; 8 | 9 | export const youtubeOptions = { 10 | method: 'GET', 11 | headers: { 12 | 'X-RapidAPI-Host': 'youtube-search-and-download.p.rapidapi.com', 13 | 'X-RapidAPI-Key': 'aedff815f2mshff4e5da66ca3a11p1d4ce8jsna3fd75571a23', 14 | }, 15 | }; 16 | 17 | export const fetchData = async (url, options) => { 18 | const res = await fetch(url, options); 19 | const data = await res.json(); 20 | 21 | return data; 22 | }; -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Box } from '@mui/material'; 3 | 4 | import Exercises from '../components/Exercises'; 5 | import SearchExercises from '../components/SearchExercises'; 6 | import HeroBanner from '../components/HeroBanner'; 7 | 8 | const Home = () => { 9 | const [exercises, setExercises] = useState([]); 10 | const [bodyPart, setBodyPart] = useState('all'); 11 | 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | }; 20 | 21 | export default Home; -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Route, Routes} from 'react-router-dom'; 3 | import { Box } from '@mui/material'; 4 | 5 | 6 | import './App.css'; 7 | import ExerciseDetail from './pages/ExerciseDetail'; 8 | import Home from './pages/Home'; 9 | import Navbar from './components/Navbar'; 10 | import Footer from './components/Footer'; 11 | 12 | 13 | const App = () => { 14 | return ( 15 | 16 | 17 | 18 | } /> 19 | } /> 20 | 21 | < Footer /> 22 | 23 | ) 24 | } 25 | 26 | export default App -------------------------------------------------------------------------------- /src/components/ExerciseCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { Button, Stack, Typography } from '@mui/material'; 4 | 5 | const ExerciseCard = ({ exercise }) => ( 6 | 7 | {exercise.name} 8 | 9 | 12 | 15 | 16 | 17 | {exercise.name} 18 | 19 | 20 | ); 21 | 22 | export default ExerciseCard; -------------------------------------------------------------------------------- /src/components/BodyPart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Stack, Typography } from '@mui/material'; 3 | import Icon from '../assets/icons/gym.png'; 4 | 5 | const BodyPart = ({ item, setBodyPart, bodyPart }) => ( 6 | { 13 | setBodyPart(item); 14 | window.scrollTo({ top: 1800, left: 100, behavior: 'smooth' }); 15 | }} 16 | > 17 | dumbbell 18 | {item} 19 | 20 | ); 21 | 22 | export default BodyPart; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gym-website", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/react": "^11.9.0", 7 | "@emotion/styled": "^11.8.1", 8 | "@mui/icons-material": "^5.6.1", 9 | "@mui/material": "^5.6.1", 10 | "react": "^18.0.0", 11 | "react-dom": "^18.0.0", 12 | "react-horizontal-scrolling-menu": "^2.7.1", 13 | "react-loader-spinner": "^6.0.0-0", 14 | "react-router-dom": "^6.3.0", 15 | "react-scripts": "5.0.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { Stack } from '@mui/material'; 4 | 5 | import Logo from '../assets/images/Logo.png'; 6 | 7 | 8 | const Navbar = () => { 9 | return ( 10 | 15 | 16 | 17 | logo 18 | 19 | 20 | 26 | Home 29 | Exercises 32 | 33 | 34 | ) 35 | } 36 | 37 | export default Navbar -------------------------------------------------------------------------------- /src/components/SimilarExercises.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Typography, Box, Stack } from '@mui/material'; 3 | 4 | import HorizontalScrollbar from './HorizontalScrollbar'; 5 | import Loader from './Loader'; 6 | 7 | const SimilarExercises = ({ targetMuscleExercises, equipmentExercises }) => ( 8 | 9 | 10 | Similar Target Muscle exercises 11 | 12 | 13 | {targetMuscleExercises.length !== 0 ? : } 14 | 15 | 16 | Similar Equipment exercises 17 | 18 | 19 | {equipmentExercises.length !== 0 ? : } 20 | 21 | 22 | ); 23 | 24 | export default SimilarExercises; -------------------------------------------------------------------------------- /src/components/HeroBanner.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Box, Stack, Typography , Button} from '@mui/material'; 3 | 4 | import HeroBannerImage from '../assets/images/banner.png'; 5 | 6 | 7 | const HeroBanner = () => { 8 | return ( 9 | 14 | 16 | Fitness Club 17 | 18 | 22 | 23 | Sweat, Smile
and Repeat 24 |
25 | 27 | Check out the most effective exercises 28 | 29 | 35 | 44 | Exercise 45 | 46 | banner 48 | 49 |
50 | ) 51 | } 52 | 53 | export default HeroBanner -------------------------------------------------------------------------------- /src/components/HorizontalScrollbar.js: -------------------------------------------------------------------------------- 1 | import React , { useContext } from 'react' 2 | 3 | import { Box, Typography } from '@mui/material'; 4 | import { ScrollMenu, VisibilityContext } from 'react-horizontal-scrolling-menu'; 5 | 6 | 7 | import BodyPart from './BodyPart'; 8 | 9 | import RightArrowIcon from '../assets/icons/right-arrow.png'; 10 | import LeftArrowIcon from '../assets/icons/left-arrow.png'; 11 | 12 | const LeftArrow = () => { 13 | const { scrollPrev } = useContext(VisibilityContext); 14 | 15 | return ( 16 | scrollPrev()} className="right-arrow"> 17 | right-arrow 18 | 19 | ); 20 | }; 21 | 22 | const RightArrow = () => { 23 | const { scrollNext } = useContext(VisibilityContext); 24 | 25 | return ( 26 | scrollNext()} className="left-arrow"> 27 | right-arrow 28 | 29 | ); 30 | }; 31 | 32 | 33 | 34 | const HorizontalScrollbar = ({ data , bodyPart, setBodyPart }) => { 35 | return ( 36 | 37 | {data.map((item) => ( 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ) 51 | 52 | )} 53 | 54 | 55 | 56 | ) 57 | } 58 | 59 | export default HorizontalScrollbar; -------------------------------------------------------------------------------- /.github/workflows/jekyll-gh-pages.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 2 | name: Deploy Jekyll with GitHub Pages dependencies preinstalled 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Build job 26 | build: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v3 31 | - name: Setup Pages 32 | uses: actions/configure-pages@v3 33 | - name: Build with Jekyll 34 | uses: actions/jekyll-build-pages@v1 35 | with: 36 | source: ./ 37 | destination: ./_site 38 | - name: Upload artifact 39 | uses: actions/upload-pages-artifact@v1 40 | 41 | # Deployment job 42 | deploy: 43 | environment: 44 | name: github-pages 45 | url: ${{ steps.deployment.outputs.page_url }} 46 | runs-on: ubuntu-latest 47 | needs: build 48 | steps: 49 | - name: Deploy to GitHub Pages 50 | id: deployment 51 | uses: actions/deploy-pages@v2 52 | -------------------------------------------------------------------------------- /src/components/ExerciseVideos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Typography, Box, Stack } from '@mui/material'; 3 | import Loader from './Loader'; 4 | 5 | const ExerciseVideos = ({ exerciseVideos, name }) => { 6 | if (!exerciseVideos.length) return ; 7 | 8 | return ( 9 | 10 | 11 | Watch {name} exercise videos 12 | 13 | 14 | {exerciseVideos?.slice(0, 3)?.map((item, index) => ( 15 | 22 | {item.video.title} 23 | 24 | 25 | {item.video.title} 26 | 27 | 28 | {item.video.channelName} 29 | 30 | 31 | 32 | ))} 33 | 34 | 35 | ); 36 | }; 37 | 38 | export default ExerciseVideos; -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Developed a feature-rich fitness application with the capability for users to choose exercise categories and target specific muscle groups. 2 | 3 |
4 | 5 |

PLAY THE VIDEO 👇🏻

6 | 7 | 8 | https://github.com/user-attachments/assets/53f4508f-d614-48be-aacc-dbd942071a11 9 | 10 |
11 | 12 | Implemented a user-friendly interface allowing users to browse through a vast database of more than one thousand exercises , complete with practical examples and detailed descriptions. 13 | 14 | ![Screenshot (103)](https://github.com/nawabsahab16/gym-website/assets/117763400/64486a26-f577-4e43-8281-d2e1726ff86e) 15 | 16 |
17 | 18 | Integrated pagination for easy navigation ,enhancing the overall user experience within the application. 19 | 20 | ![Screenshot (110)](https://github.com/nawabsahab16/gym-website/assets/117763400/ef2b8c05-2cfa-4cc9-bf0c-4830a7c6d4b4) 21 |
22 | 23 | ![Screenshot (108)](https://github.com/nawabsahab16/gym-website/assets/117763400/f18bb8b7-6409-4cec-b76c-a2575c114da2) 24 | 25 | Incorporated a dynamic feature to fetch exercise-related videos from YouTube, providing users with visual demonstrations for each exercise. 26 |
27 | 28 | ![Screenshot (107)](https://github.com/nawabsahab16/gym-website/assets/117763400/f0f3fdd5-19fa-4f9b-9dbf-23c31c29bb6f) 29 |
30 | 31 | Implemented a system to display similar exercises, offering users a diversified and personalized workout selection. 32 | 33 |
34 | 35 | ![Screenshot (106)](https://github.com/nawabsahab16/gym-website/assets/117763400/5eba15df-c5f1-452f-8f86-065d76dd7532) 36 |
37 | 38 | 39 | ![Screenshot (105)](https://github.com/nawabsahab16/gym-website/assets/117763400/e2200b19-6c66-49cd-b2eb-b5ac08ec0adb) 40 | 41 | Demonstrated strong skills in software development, user experience design, and the integration of external APIs, contributing to the creation of a robust and comprehensive fitness platform. 42 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | }, 6 | extends: [ 7 | 'plugin:react/recommended', 8 | 'airbnb', 9 | ], 10 | parserOptions: { 11 | ecmaFeatures: { 12 | jsx: true, 13 | }, 14 | ecmaVersion: 12, 15 | sourceType: 'module', 16 | }, 17 | plugins: [ 18 | 'react', 19 | ], 20 | rules: { 21 | 22 | 'react/function-component-definition': 0, 23 | 'import/extensions': 0, 24 | 'react/prop-types': 0, 25 | 'linebreak-style': 0, 26 | 'react/state-in-constructor': 0, 27 | 'import/prefer-default-export': 0, 28 | 'max-len': [ 29 | 2, 30 | 550, 31 | ], 32 | 33 | 'no-multiple-empty-lines': [ 34 | 'error', 35 | { 36 | max: 1, 37 | maxEOF: 1, 38 | }, 39 | ], 40 | 'no-underscore-dangle': [ 41 | 'error', 42 | { 43 | allow: [ 44 | '_d', 45 | '_dh', 46 | '_h', 47 | '_id', 48 | '_m', 49 | '_n', 50 | '_t', 51 | '_text', 52 | ], 53 | }, 54 | ], 55 | 'object-curly-newline': 0, 56 | 'react/jsx-filename-extension': 0, 57 | 'react/jsx-one-expression-per-line': 0, 58 | 'jsx-a11y/click-events-have-key-events': 0, 59 | 'jsx-a11y/alt-text': 0, 60 | 'jsx-a11y/no-autofocus': 0, 61 | 'jsx-a11y/no-static-element-interactions': 0, 62 | 'react/no-array-index-key': 0, 63 | 'jsx-a11y/anchor-is-valid': [ 64 | 'error', 65 | { 66 | components: [ 67 | 'Link', 68 | ], 69 | specialLink: [ 70 | 'to', 71 | 'hrefLeft', 72 | 'hrefRight', 73 | ], 74 | aspects: [ 75 | 'noHref', 76 | 'invalidHref', 77 | 'preferButton', 78 | ], 79 | }, 80 | ], 81 | }, 82 | }; -------------------------------------------------------------------------------- /src/components/Detail.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Typography, Stack, Button } from '@mui/material'; 3 | 4 | import BodyPartImage from '../assets/icons/body-part.png'; 5 | import TargetImage from '../assets/icons/target.png'; 6 | import EquipmentImage from '../assets/icons/equipment.png'; 7 | 8 | const Detail = ({ exerciseDetail }) => { 9 | const { bodyPart, gifUrl, name, target, equipment } = exerciseDetail; 10 | 11 | const extraDetail = [ 12 | { 13 | icon: BodyPartImage, 14 | name: bodyPart, 15 | }, 16 | { 17 | icon: TargetImage, 18 | name: target, 19 | }, 20 | { 21 | icon: EquipmentImage, 22 | name: equipment, 23 | }, 24 | ]; 25 | 26 | return ( 27 | 28 | {name} 29 | 30 | 31 | {name} 32 | 33 | 34 | Exercises keep you strong.{' '} 35 | {name} bup is one 36 | of the best
exercises to target your {target}. It will help you improve your{' '} 37 |
mood and gain energy. 38 |
39 | {extraDetail?.map((item) => ( 40 | 41 | 44 | 45 | {item.name} 46 | 47 | 48 | ))} 49 |
50 |
51 | ); 52 | }; 53 | 54 | export default Detail; -------------------------------------------------------------------------------- /src/pages/ExerciseDetail.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useParams } from 'react-router-dom'; 3 | import { Box } from '@mui/material'; 4 | 5 | import { exerciseOptions, fetchData, youtubeOptions } from '../utils/fetchData'; 6 | import Detail from '../components/Detail'; 7 | import ExerciseVideos from '../components/ExerciseVideos'; 8 | import SimilarExercises from '../components/SimilarExercises'; 9 | 10 | const ExerciseDetail = () => { 11 | const [exerciseDetail, setExerciseDetail] = useState({}); 12 | const [exerciseVideos, setExerciseVideos] = useState([]); 13 | const [targetMuscleExercises, setTargetMuscleExercises] = useState([]); 14 | const [equipmentExercises, setEquipmentExercises] = useState([]); 15 | const { id } = useParams(); 16 | 17 | useEffect(() => { 18 | window.scrollTo({ top: 0, behavior: 'smooth' }); 19 | 20 | const fetchExercisesData = async () => { 21 | const exerciseDbUrl = 'https://exercisedb.p.rapidapi.com'; 22 | const youtubeSearchUrl = 'https://youtube-search-and-download.p.rapidapi.com'; 23 | 24 | const exerciseDetailData = await fetchData(`${exerciseDbUrl}/exercises/exercise/${id}`, exerciseOptions); 25 | setExerciseDetail(exerciseDetailData); 26 | 27 | const exerciseVideosData = await fetchData(`${youtubeSearchUrl}/search?query=${exerciseDetailData.name} exercise`, youtubeOptions); 28 | setExerciseVideos(exerciseVideosData.contents); 29 | 30 | const targetMuscleExercisesData = await fetchData(`${exerciseDbUrl}/exercises/target/${exerciseDetailData.target}`, exerciseOptions); 31 | setTargetMuscleExercises(targetMuscleExercisesData); 32 | 33 | const equimentExercisesData = await fetchData(`${exerciseDbUrl}/exercises/equipment/${exerciseDetailData.equipment}`, exerciseOptions); 34 | setEquipmentExercises(equimentExercisesData); 35 | }; 36 | 37 | fetchExercisesData(); 38 | }, [id]); 39 | 40 | if (!exerciseDetail) return
No Data
; 41 | 42 | return ( 43 | 44 | 45 | 46 | 47 | 48 | ); 49 | }; 50 | 51 | export default ExerciseDetail; -------------------------------------------------------------------------------- /src/components/Exercises.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import Pagination from '@mui/material/Pagination'; 3 | import { Box, Stack, Typography } from '@mui/material'; 4 | 5 | import { exerciseOptions, fetchData } from '../utils/fetchData'; 6 | import ExerciseCard from './ExerciseCard'; 7 | import Loader from './Loader'; 8 | 9 | const Exercises = ({ exercises, setExercises, bodyPart }) => { 10 | const [currentPage, setCurrentPage] = useState(1); 11 | const [exercisesPerPage] = useState(6); 12 | 13 | useEffect(() => { 14 | const fetchExercisesData = async () => { 15 | let exercisesData = []; 16 | 17 | if (bodyPart === 'all') { 18 | exercisesData = await fetchData('https://exercisedb.p.rapidapi.com/exercises', exerciseOptions); 19 | } else { 20 | exercisesData = await fetchData(`https://exercisedb.p.rapidapi.com/exercises/bodyPart/${bodyPart}`, exerciseOptions); 21 | } 22 | 23 | setExercises(exercisesData); 24 | }; 25 | 26 | fetchExercisesData(); 27 | }, [bodyPart]); 28 | 29 | // Pagination 30 | const indexOfLastExercise = currentPage * exercisesPerPage; 31 | const indexOfFirstExercise = indexOfLastExercise - exercisesPerPage; 32 | const currentExercises = exercises.slice(indexOfFirstExercise, indexOfLastExercise); 33 | 34 | const paginate = (event, value) => { 35 | setCurrentPage(value); 36 | 37 | window.scrollTo({ top: 1800, behavior: 'smooth' }); 38 | }; 39 | 40 | if (!currentExercises.length) return ; 41 | 42 | return ( 43 | 44 | Showing Results 45 | 46 | {currentExercises.map((exercise, idx) => ( 47 | 48 | ))} 49 | 50 | 51 | {exercises.length > 9 && ( 52 | 61 | )} 62 | 63 | 64 | ); 65 | }; 66 | 67 | export default Exercises; 68 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Josefin Sans'; 3 | background-color: #FFFAFB; 4 | } 5 | 6 | * { 7 | padding: 0px; 8 | margin: 0px; 9 | box-sizing: border-box; 10 | 11 | } 12 | 13 | ::-webkit-scrollbar { 14 | width: 0px; 15 | } 16 | 17 | .right-arrow, 18 | .left-arrow { 19 | cursor: pointer; 20 | background: transparent; 21 | outline: none; 22 | border: none; 23 | display: flex; 24 | justify-content: center; 25 | align-items: center; 26 | color: #FF2625; 27 | font-size: 25px; 28 | border-radius: 4px; 29 | position: absolute; 30 | bottom: -20px; 31 | right: 80px; 32 | transform: scale(1, 1); 33 | transition: 0.3s all ease-in-out; 34 | } 35 | 36 | .right-arrow { 37 | right: 140px; 38 | } 39 | 40 | .right-arrow:hover, 41 | .left-arrow:hover { 42 | transform: scale(1.3, 1.3); 43 | } 44 | 45 | .react-horizontal-scrolling-menu--wrapper { 46 | width: 100%; 47 | display: flex; 48 | flex-wrap: wrap; 49 | } 50 | 51 | .detail-image { 52 | width: 729px; 53 | height: 742px; 54 | } 55 | 56 | .hero-banner-img { 57 | position: absolute; 58 | right: 40px; 59 | top: 0px; 60 | width: 700px; 61 | height: 900px; 62 | margin-top: -330px; 63 | 64 | } 65 | 66 | .exercise-card { 67 | width: 400px; 68 | height: 445px; 69 | background: #fff; 70 | border-top: 4px solid #FF2625; 71 | border-bottom-left-radius: 20px; 72 | text-decoration: none; 73 | display: flex; 74 | justify-content: space-between; 75 | flex-direction: column; 76 | padding-bottom: 10px; 77 | transform: scale(1, 1); 78 | transition: 0.3s all ease-in-out; 79 | } 80 | 81 | .exercise-card img { 82 | height: 326px; 83 | } 84 | 85 | .bodyPart-card { 86 | transform: scale(1, 1); 87 | transition: 0.3s all ease-in-out; 88 | } 89 | 90 | .exercise-card:hover, 91 | .bodyPart-card:hover { 92 | transform: scale(1.1, 1.1); 93 | } 94 | 95 | .search-btn:hover { 96 | color: #FF2625 !important; 97 | border: 1px solid #FF2625 !important; 98 | } 99 | 100 | .exercise-video { 101 | display: flex; 102 | flex-direction: column; 103 | gap: 24px; 104 | width: 387px; 105 | height: 381px; 106 | text-decoration: none; 107 | } 108 | 109 | @media screen and (max-width:1200px) { 110 | .detail-image { 111 | width: 300px; 112 | height: 300px; 113 | } 114 | 115 | 116 | .react-horizontal-scrolling-menu--scroll-container { 117 | width: 500px; 118 | } 119 | 120 | .left-arrow, 121 | .right-arrow { 122 | position: static !important; 123 | } 124 | 125 | .hero-banner-img { 126 | display: none; 127 | } 128 | 129 | .exercise-card { 130 | width: 320px; 131 | } 132 | 133 | .exercise-video { 134 | width: 320px; 135 | height: 300px; 136 | } 137 | 138 | } 139 | 140 | @media screen and (max-width:400px) { 141 | .exercise-card { 142 | width: 280px; 143 | } 144 | } -------------------------------------------------------------------------------- /src/components/SearchExercises.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState} from 2 | 'react'; 3 | 4 | import {Box, Button, Stack , TextField, Typography} from '@mui/material'; 5 | 6 | import { exerciseOptions, fetchData} from '../utils/fetchData'; 7 | 8 | import HorizontalScrollbar from './HorizontalScrollbar'; 9 | 10 | 11 | 12 | 13 | 14 | const SearchExercises = ({setExercises, bodyPart, setBodyPart}) => { 15 | const [search, setSearch]= useState('') 16 | 17 | const [bodyParts, setBodyParts] = useState([]) 18 | 19 | 20 | 21 | useEffect(() => { 22 | const fetchExercisesData = async () => { 23 | const bodyPartData = await fetchData('https://exercisedb.p.rapidapi.com/exercises/bodyPartList', exerciseOptions); 24 | 25 | setBodyParts(['all ', ...bodyPartData]); 26 | 27 | } 28 | 29 | fetchExercisesData(); 30 | 31 | }, []) 32 | 33 | 34 | 35 | 36 | 37 | const handleSearch = async () => { 38 | if(search) { 39 | const exercisesData = await fetchData( 40 | 'https://exercisedb.p.rapidapi.com/exercises' , 41 | exerciseOptions ); 42 | 43 | const searchedExercises = exercisesData.filter( 44 | (exercise) => exercise.name.toLowerCase().includes(search) 45 | || exercise.target.toLowerCase().includes(search) 46 | || exercise.equipment.toLowerCase().includes(search) 47 | || exercise.bodyPart.toLowerCase().includes(search) 48 | ) 49 | 50 | setSearch(''); 51 | setExercises(searchedExercises); 52 | 53 | 54 | } 55 | } 56 | 57 | 58 | 59 | return ( 60 | 62 | 65 | Awesome Exercise You
66 | Should Know 67 |
68 | 69 | setSearch(e.target.value.toLowerCase())} 79 | placeholder="Search Exercises" 80 | type="text" 81 | /> 82 | 99 | 100 | 101 | 102 | 103 | 104 | 107 | 108 | 109 | 110 | 111 | 112 |
113 | ) 114 | } 115 | 116 | export default SearchExercises --------------------------------------------------------------------------------