├── .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 |
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 |
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 |
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 |
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 |
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 |
18 |
19 | );
20 | };
21 |
22 | const RightArrow = () => {
23 | const { scrollNext } = useContext(VisibilityContext);
24 |
25 | return (
26 | scrollNext()} className="left-arrow">
27 |
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 |
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 | 
15 |
16 |
17 |
18 | Integrated pagination for easy navigation ,enhancing the overall user experience within the application.
19 |
20 | 
21 |
22 |
23 | 
24 |
25 | Incorporated a dynamic feature to fetch exercise-related videos from YouTube, providing users with visual demonstrations for each exercise.
26 |
27 |
28 | 
29 |
30 |
31 | Implemented a system to display similar exercises, offering users a diversified and personalized workout selection.
32 |
33 |
34 |
35 | 
36 |
37 |
38 |
39 | 
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 |
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
--------------------------------------------------------------------------------