diff --git a/fitness_tracker/package-lock.json b/fitness_tracker/package-lock.json
index cbf84b9..1677918 100644
--- a/fitness_tracker/package-lock.json
+++ b/fitness_tracker/package-lock.json
@@ -12,6 +12,7 @@
"@emotion/styled": "^11.13.0",
"@material/tab": "^14.0.0",
"@material/tabs": "^2.3.0",
+ "@mui/icons-material": "^6.1.2",
"@mui/material": "^5.16.7",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
@@ -1963,9 +1964,10 @@
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
},
"node_modules/@babel/runtime": {
- "version": "7.25.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz",
- "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==",
+ "version": "7.25.7",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz",
+ "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==",
+ "license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@@ -3610,6 +3612,32 @@
"url": "https://opencollective.com/mui-org"
}
},
+ "node_modules/@mui/icons-material": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.2.tgz",
+ "integrity": "sha512-7NNcjW5JoT9jHagrVbARA1o41vQY2xezDamtke+mEKKZmsJyejfRBOacSrPDfjZQ//lyhIjNKyzAwisxYJR47w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.25.6"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@mui/material": "^6.1.2",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@mui/material": {
"version": "5.16.7",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.7.tgz",
@@ -4262,104 +4290,6 @@
"url": "https://github.com/sponsors/gregberge"
}
},
- "node_modules/@testing-library/dom": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
- "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
- "peer": true,
- "dependencies": {
- "@babel/code-frame": "^7.10.4",
- "@babel/runtime": "^7.12.5",
- "@types/aria-query": "^5.0.1",
- "aria-query": "5.3.0",
- "chalk": "^4.1.0",
- "dom-accessibility-api": "^0.5.9",
- "lz-string": "^1.5.0",
- "pretty-format": "^27.0.2"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@testing-library/dom/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "peer": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/@testing-library/dom/node_modules/aria-query": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
- "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
- "peer": true,
- "dependencies": {
- "dequal": "^2.0.3"
- }
- },
- "node_modules/@testing-library/dom/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "peer": true,
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/@testing-library/dom/node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "peer": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/@testing-library/dom/node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "peer": true
- },
- "node_modules/@testing-library/dom/node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@testing-library/dom/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "peer": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/@testing-library/jest-dom": {
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz",
@@ -7752,15 +7682,6 @@
"node": ">= 0.8"
}
},
- "node_modules/dequal": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -18254,19 +18175,6 @@
"is-typedarray": "^1.0.0"
}
},
- "node_modules/typescript": {
- "version": "4.9.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
- "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
- "peer": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=4.2.0"
- }
- },
"node_modules/unbox-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
diff --git a/fitness_tracker/package.json b/fitness_tracker/package.json
index 3e176a1..5eb2ae1 100644
--- a/fitness_tracker/package.json
+++ b/fitness_tracker/package.json
@@ -7,6 +7,7 @@
"@emotion/styled": "^11.13.0",
"@material/tab": "^14.0.0",
"@material/tabs": "^2.3.0",
+ "@mui/icons-material": "^6.1.2",
"@mui/material": "^5.16.7",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
diff --git a/fitness_tracker/src/components/ExerciseAdder.jsx b/fitness_tracker/src/components/ExerciseAdder.jsx
index 2698b70..ceb70e5 100644
--- a/fitness_tracker/src/components/ExerciseAdder.jsx
+++ b/fitness_tracker/src/components/ExerciseAdder.jsx
@@ -1,69 +1,112 @@
import React, { useState } from 'react';
-import buttons from '../module_CSS/buttons.module.css'
-import styles from '../module_CSS/ExerciseLogger.module.css'
-import { MenuItem, Select } from '@mui/material';
+import buttons from '../module_CSS/buttons.module.css';
+import styles from '../module_CSS/ExerciseLogger.module.css';
+import { MenuItem, Select, Tooltip } from '@mui/material';
-const ExerciseAdder = ({exerciseList, addExercise, cancelAddExercise }) => {
- const [selectedExercise, setSelectedExercise] = useState(exerciseList[0])
+const ExerciseAdder = ({ exerciseList, addExercise, cancelAddExercise }) => {
+ const [selectedExercise, setSelectedExercise] = useState(exerciseList.length > 0 ? exerciseList[0] : { name: "No exercises added" });
-
return (
-
+
+
+
+ ) : (
+
+ )}
|
- {selectedExercise.weight}
+ {selectedExercise.weight || 0}
|
- {selectedExercise.reps}
+ {selectedExercise.reps || 0}
|
- {selectedExercise.setsGoal}
+ {selectedExercise.setsGoal || 0}
|
0
|
- |
- |
+
+
+ |
+
+
+ |
- )
-}
+ );
+};
export default ExerciseAdder;
diff --git a/fitness_tracker/src/components/ExercisesDisplay.jsx b/fitness_tracker/src/components/ExercisesDisplay.jsx
index f4797e1..4924523 100644
--- a/fitness_tracker/src/components/ExercisesDisplay.jsx
+++ b/fitness_tracker/src/components/ExercisesDisplay.jsx
@@ -51,16 +51,16 @@ const ExercisesDisplay = ({ exercises, setExercises }) => {
const logWorkout = () => {
// Calculate the score of the workout
- let score = 0
+ let volumeScore = 0
exercises.forEach((exercise, index )=> {
- score += (( Number(setsLogged[index]) / Number(exercise.setsGoal)) * Number(exercise.weight))
+ volumeScore += (( Number(setsLogged[index]) / Number(exercise.setsGoal)) * Number(exercise.weight) * Number(exercise.reps))
})
- score = Math.round(score)
+ volumeScore = Math.round(volumeScore)
// Create workout in the database with only score and date
- axios.post("http://localhost:4001/workout", { score: score, date: new Date() }, { withCredentials: true })
+ axios.post("http://localhost:4001/workout", { score: volumeScore, date: new Date() }, { withCredentials: true })
.then((response) => {
console.log("Workout created", response.data)
diff --git a/fitness_tracker/src/components/RoutinesDisplay.jsx b/fitness_tracker/src/components/RoutinesDisplay.jsx
index f5781a3..6000849 100644
--- a/fitness_tracker/src/components/RoutinesDisplay.jsx
+++ b/fitness_tracker/src/components/RoutinesDisplay.jsx
@@ -1,13 +1,13 @@
-import React, { useEffect, useState } from 'react';
-import Routine from './Routine';
-import NewRoutineModal from './NewRoutineModal';
-import styles from '../module_CSS/RoutinesDisplay.module.css'
-import axios from 'axios';
+import React, { useEffect, useState } from "react";
+import Routine from "./Routine";
+import NewRoutineModal from "./NewRoutineModal";
+import styles from "../module_CSS/RoutinesDisplay.module.css";
+import axios from "axios";
const RoutinesDisplay = ({ onAddToTodayWorkout }) => {
- const [routines, setRoutines] = useState([]);
+ const [routines, setRoutines] = useState([]);
- /*
+ /*
{
name: "Leg Routine",
date: "14th Aug",
@@ -20,181 +20,225 @@ const RoutinesDisplay = ({ onAddToTodayWorkout }) => {
}
*/
- const [isModalOpen, setIsModalOpen] = useState(false);
+ const [isModalOpen, setIsModalOpen] = useState(false);
- // Get routines from the backend
- useEffect(() => {
- axios.get("http://localhost:4001/routine", { withCredentials: true, })
+ // Get routines from the backend
+ useEffect(() => {
+ axios
+ .get("http://localhost:4001/routine", { withCredentials: true })
.then((response) => {
response.data.forEach((routine) => {
- fetchExercises(routine);
+ fetchExercises(routine);
});
// Routine exercises are added in the fetchExercises function, so we can just set the routines here
setRoutines(response.data);
-
})
.catch((error) => {
handleAuthError(error);
console.error("An error occurred while fetching routines:", error);
});
- } , []);
-
- // Get exercises for a routine, and add them to the routine object
- const fetchExercises = (routine) => {
- // Get the exercise IDs for the routine
- axios.get(`http://localhost:4001/routine/${routine.id}/exercises`, { withCredentials: true, })
+ }, []);
+
+ // Get exercises for a routine, and add them to the routine object
+ const fetchExercises = (routine) => {
+ // Get the exercise IDs for the routine
+ axios
+ .get(`http://localhost:4001/routine/${routine.id}/exercises`, {
+ withCredentials: true,
+ })
.then((response) => {
const exerciseIds = response.data.exerciseIds.join(","); // Make comma separated string of exercise IDs
// Get the actual exercises info for the routine
- axios.get(`http://localhost:4001/exercise/${exerciseIds}`, { withCredentials: true, })
+ axios
+ .get(`http://localhost:4001/exercise/${exerciseIds}`, {
+ withCredentials: true,
+ })
.then((response) => {
routine.exercises = response.data;
})
.catch((error) => {
handleAuthError(error);
console.error("An error occurred while fetching exercises:", error);
- }
- );
+ });
})
.catch((error) => {
handleAuthError(error);
console.error("An error occurred while fetching exercise ids:", error);
});
- };
-
- const addExerciseToRoutine = (routine, exercise) => {
- // Add the exercise to the routine in the backend
- axios.post("http://localhost:4001/exercise", { "routine_id": routine.id, ...exercise }, { withCredentials: true, })
- .then((response) => {
- // Get the exercise ID
- const exerciseId = response.data.id;
-
- // Add the exercise ID to the routine object
- routine.exercises.forEach((exercise) => {
- if (exercise.name === response.data.name) {
- exercise.id = exerciseId;
- }
- })
- })
- .catch((error) => {
- handleAuthError(error);
- console.error("An error occurred while adding exercises:", error);
- });
- };
+ };
+
+ const addExerciseToRoutine = (routine, exercise) => {
+ // Add the exercise to the routine in the backend
+ axios
+ .post(
+ "http://localhost:4001/exercise",
+ { routine_id: routine.id, ...exercise },
+ { withCredentials: true }
+ )
+ .then((response) => {
+ // Get the exercise ID
+ const exerciseId = response.data.id.id;
- const handleSaveRoutine = (newRoutine) => {
- setIsModalOpen(false);
+ routine.exercises.forEach((e) => {
+ if (e.name === exercise.name) {
+ e.id = exerciseId;
+ }
+ });
+ })
+ .catch((error) => {
+ handleAuthError(error);
+ console.error("An error occurred while adding exercises:", error);
+ });
+ };
- // Extract routine only data
- const { name, muscles, date} = newRoutine;
+ const handleSaveRoutine = (newRoutine) => {
+ setIsModalOpen(false);
- // Extract exercises only data
- const exercises = newRoutine.exercises.map(({ name, setsGoal, reps, weight }) => ({ name, setsGoal, reps, weight }));
+ // Extract routine only data
+ const { name, muscles, date } = newRoutine;
- // Create new routine object with no exercises
- axios.post("http://localhost:4001/routine", {"name": name, "muscles": muscles, "date": date}, { withCredentials: true, })
- .then((response) => {
- // Get the routine ID from the response and add it to the newRoutine object
- const routineId = response.data.id.id;
- newRoutine.id = routineId;
+ // Extract exercises only data
+ const exercises = newRoutine.exercises.map(
+ ({ name, setsGoal, reps, weight }) => ({ name, setsGoal, reps, weight })
+ );
- // Add exercises to the routine in the backend
- exercises.forEach((exercise) => {
- addExerciseToRoutine(newRoutine, exercise);
- });
- // Add the newRoutine object to the routines array in the frontend so that it is displayed immediately
- setRoutines([...routines, newRoutine]);
- }
+ // Create new routine object with no exercises
+ axios
+ .post(
+ "http://localhost:4001/routine",
+ { name: name, muscles: muscles, date: date },
+ { withCredentials: true }
)
+ .then((response) => {
+ // Get the routine ID from the response and add it to the newRoutine object
+ const routineId = response.data.id.id;
+ newRoutine.id = routineId;
+
+ // Add exercises to the routine in the backend
+ exercises.forEach((exercise) => {
+ addExerciseToRoutine(newRoutine, exercise);
+ });
+ // Add the newRoutine object to the routines array in the frontend so that it is displayed immediately
+ setRoutines([...routines, newRoutine]);
+ })
.catch((error) => {
handleAuthError(error);
console.error("An error occurred while adding routine:", error);
- });
- };
-
- const handleDeleteRoutine = (routineToDelete) => {
- // Delete the routine in the backend
- axios.delete(`http://localhost:4001/routine/${routineToDelete.id}`, { withCredentials: true, })
- .then((response) => {
- // Delete the routine in the frontend
- setRoutines(routines.filter(routine => routine !== routineToDelete));
- })
- .catch((error) => {
- handleAuthError(error);
- console.error("An error occurred while deleting routine:", error);
- });
- };
-
- const handleEditRoutine = (updatedRoutine, index) => {
- // Update the routine in the backend
- const { id, name, muscles, date } = updatedRoutine;
- // Update the routine information
- axios.put(`http://localhost:4001/routine/${id}`, { "name": name, "muscles": muscles, "date": date }, { withCredentials: true, })
- .then((response) => {
- // Update the exercises in the backend
- updatedRoutine.exercises.forEach((exercise) => {
- axios.patch(`http://localhost:4001/exercise/${exercise.id}`, { "name": exercise.name, "setsGoal": exercise.setsGoal, "reps": exercise.reps, "weight": exercise.weight, "routine_id": id }, { withCredentials: true, })
- .then((response) => {
- // Exercise updated successfully, do nothing
- })
- .catch((error) => {
- handleAuthError(error);
- if (error.response.status === 404) {
- // Exercise not found, so we need to create a new exercise
- addExerciseToRoutine(updatedRoutine, exercise);
- } else {
- console.error("An error occurred while updating exercises:", error);
- }
+ });
+ };
+
+ const handleDeleteRoutine = (routineToDelete) => {
+ // Delete the routine in the backend
+ axios
+ .delete(`http://localhost:4001/routine/${routineToDelete.id}`, {
+ withCredentials: true,
+ })
+ .then((response) => {
+ // Delete the routine in the frontend
+ setRoutines(routines.filter((routine) => routine !== routineToDelete));
+ })
+ .catch((error) => {
+ handleAuthError(error);
+ console.error("An error occurred while deleting routine:", error);
+ });
+ };
+
+ const handleEditRoutine = (updatedRoutine, index) => {
+ // Update the routine in the backend
+ const { id, name, muscles, date } = updatedRoutine;
+ // Update the routine information
+ axios
+ .put(
+ `http://localhost:4001/routine/${id}`,
+ { name: name, muscles: muscles, date: date },
+ { withCredentials: true }
+ )
+ .then((response) => {
+ // Update the exercises in the backend
+ updatedRoutine.exercises.forEach((exercise) => {
+ axios
+ .patch(
+ `http://localhost:4001/exercise/${exercise.id}`,
+ {
+ name: exercise.name,
+ setsGoal: exercise.setsGoal,
+ reps: exercise.reps,
+ weight: exercise.weight,
+ routine_id: id,
+ },
+ { withCredentials: true }
+ )
+ .then((response) => {
+ // Exercise updated successfully, do nothing
+ })
+ .catch((error) => {
+ handleAuthError(error);
+ if (error.response.status === 404) {
+ // Exercise not found, so we need to create a new exercise
+ addExerciseToRoutine(updatedRoutine, exercise);
+ } else {
+ console.error(
+ "An error occurred while updating exercises:",
+ error
+ );
+ }
});
- });
- })
- .catch((error) => {
- handleAuthError(error);
- console.error("An error occurred while updating routine:", error);
- }
- );
+ });
+ })
+ .catch((error) => {
+ handleAuthError(error);
+ console.error("An error occurred while updating routine:", error);
+ });
- // Update the routine in the frontend
- setRoutines(routines.map((routine, i) => i === index ? updatedRoutine : routine));
- };
-
- const handleAuthError = (error) => {
- if (error.response && error.response.status === 401) {
- alert("Not logged in (Or an error has occurred), cannot access dashboard");
- window.location.href = "/login";
- }
- };
-
- return (
-
-
Workout Routines
- {isModalOpen ?
- :
-
- }
- {isModalOpen &&
- setIsModalOpen(false)}
- />
- }
- {routines.map((routine, index) => (
- handleEditRoutine(updatedRoutine, index)}
- onDelete={() => handleDeleteRoutine(routine)}
- onAddToToday={() => onAddToTodayWorkout(routine)}
- />
- ))}
-
+ // Update the routine in the frontend
+ setRoutines(
+ routines.map((routine, i) => (i === index ? updatedRoutine : routine))
);
+ };
+
+ const handleAuthError = (error) => {
+ if (error.response && error.response.status === 401) {
+ alert(
+ "Not logged in (Or an error has occurred), cannot access dashboard"
+ );
+ window.location.href = "/login";
+ }
+ };
+
+ return (
+
+
Workout Routines
+ {isModalOpen ? (
+
+ ) : (
+
+ )}
+ {isModalOpen && (
+ setIsModalOpen(false)}
+ />
+ )}
+ {routines.map((routine, index) => (
+ handleEditRoutine(updatedRoutine, index)}
+ onDelete={() => handleDeleteRoutine(routine)}
+ onAddToToday={() => onAddToTodayWorkout(routine)}
+ />
+ ))}
+
+ );
};
export default RoutinesDisplay;
diff --git a/fitness_tracker/src/components/TabDisplay.jsx b/fitness_tracker/src/components/TabDisplay.jsx
index 521cf28..82628be 100644
--- a/fitness_tracker/src/components/TabDisplay.jsx
+++ b/fitness_tracker/src/components/TabDisplay.jsx
@@ -6,6 +6,7 @@ import Tab from '@mui/material/Tab';
import Box from '@mui/material/Box';
import RoutinesDisplay from './RoutinesDisplay';
import ExercisesDisplay from './ExercisesDisplay';
+import WorkoutHistoryDisplay from './WorkoutHistoryDisplay';
import styles from '../module_CSS/TabDisplay.module.css';
function CustomTabPanel(props) {
@@ -73,6 +74,11 @@ function TabDisplay() {
label="Today's Workout"
{...a11yProps(1)}
/>
+
@@ -83,6 +89,10 @@ function TabDisplay() {
+
+
+
+
);
diff --git a/fitness_tracker/src/components/WorkoutHistoryDisplay.jsx b/fitness_tracker/src/components/WorkoutHistoryDisplay.jsx
new file mode 100644
index 0000000..de460de
--- /dev/null
+++ b/fitness_tracker/src/components/WorkoutHistoryDisplay.jsx
@@ -0,0 +1,91 @@
+import React, { useState, useEffect } from "react";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ Paper,
+} from "@mui/material";
+import axios from "axios";
+import WorkoutHistoryRow from "./WorkoutHistoryRow";
+import styles from "../module_CSS/WorkoutHistory.module.css";
+
+function WorkoutHistoryDisplay() {
+ const [workouts, setWorkouts] = useState([]);
+ const [exerciseList, setExerciseList] = useState({});
+
+ // Fetch workouts and its associated exercises on component mount
+ useEffect(() => {
+ fetchWorkouts();
+ }, []);
+
+ const fetchExercises = async (workoutId) => {
+ try {
+ const response = await axios.get(
+ `http://localhost:4001/workout/${workoutId}/exercises`,
+ { withCredentials: true }
+ );
+ const exercisesData = response.data;
+ setExerciseList((prevState) => ({
+ ...prevState,
+ [workoutId]: exercisesData, // Map exercises by workout ID
+ }));
+ } catch (error) {
+ console.error(
+ `Error fetching exercises for workout ${workoutId}:`,
+ error
+ );
+ }
+ };
+
+ const fetchWorkouts = async () => {
+ try {
+ const response = await axios.get("http://localhost:4001/workout", {
+ withCredentials: true,
+ });
+ const workoutsData = response.data;
+ setWorkouts(workoutsData);
+
+ // Fetch exercises for each workout
+ workoutsData.forEach((workout) => {
+ fetchExercises(workout.id);
+ });
+ } catch (error) {
+ console.error("Error fetching workouts:", error);
+ }
+ };
+
+ return (
+
+
+
+
+
+ Workout Date
+
+
+
+
+
+ {workouts.map((workout) => (
+
+ ))}
+
+
+
+ );
+}
+
+export default WorkoutHistoryDisplay;
diff --git a/fitness_tracker/src/components/WorkoutHistoryRow.jsx b/fitness_tracker/src/components/WorkoutHistoryRow.jsx
new file mode 100644
index 0000000..292b479
--- /dev/null
+++ b/fitness_tracker/src/components/WorkoutHistoryRow.jsx
@@ -0,0 +1,169 @@
+import React, { useState } from "react";
+import {
+ TableRow,
+ TableCell,
+ IconButton,
+ Box,
+ Table,
+ TableBody,
+ TableContainer,
+ TableHead,
+ Collapse,
+ Typography,
+} from "@mui/material";
+import { KeyboardArrowDown, KeyboardArrowUp } from "@mui/icons-material";
+import styles from "../module_CSS/WorkoutHistory.module.css";
+
+const WorkoutHistoryRow = ({ workout, exercises }) => {
+ const [showDetails, setShowDetails] = useState(false);
+
+ // Toggle expanded view containing exercise details
+ const handleRowClick = () => {
+ setShowDetails(!showDetails);
+ };
+
+ const getExerciseScore = (exercise) => {
+ return (
+ (exercise.sets_completed / exercise.setsGoal) *
+ exercise.reps *
+ exercise.weight
+ ).toFixed(2);
+ };
+
+ return (
+ <>
+ {/* Workout Date Row (Collapsed View) */}
+
+
+ {new Date(workout.date).toLocaleString()}
+
+
+
+ {showDetails ? : }
+
+
+
+
+ {/* Expanded View */}
+
+
+
+
+ {/* Total Score Display */}
+
+ TOTAL SCORE: {workout.score}
+
+
+ {/* Exercise Breakdown */}
+
+
+
+
+
+ Exercise
+
+
+ Sets Completed
+
+
+ Reps
+
+
+ Weight (kg)
+
+
+ Score
+
+
+
+
+ {exercises && exercises.length > 0 ? (
+ exercises.map((exercise, index) => (
+
+
+ {exercise.name}
+
+
+ {exercise.sets_completed} / {exercise.setsGoal}
+
+
+ {exercise.reps}
+
+
+ {exercise.weight}
+
+
+ {getExerciseScore(exercise)}
+
+
+ ))
+ ) : (
+
+
+ No exercises found for this workout.
+
+
+ )}
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default WorkoutHistoryRow;
diff --git a/fitness_tracker/src/module_CSS/WorkoutHistory.module.css b/fitness_tracker/src/module_CSS/WorkoutHistory.module.css
new file mode 100644
index 0000000..8cc8b0c
--- /dev/null
+++ b/fitness_tracker/src/module_CSS/WorkoutHistory.module.css
@@ -0,0 +1,87 @@
+@import './colours.module.css'; /* Import colors from the same file */
+
+:root {
+ --fs-xl: clamp(2rem, 2vw + 1rem, 4rem);
+ --fs-600: clamp(1.5rem, 1.8vw + 1rem, 3rem);
+ --fs-400: clamp(1rem, 1.5vw + 0.8rem, 2rem);
+ --fs-300: clamp(0.8rem, 1.2vw + 0.6rem, 1.4rem);
+ --fs-200: clamp(0.7rem, 1vw + 0.5rem, 1.2rem);
+ --fs-100: clamp(0.6rem, 0.8vw + 0.4rem, 1rem);
+}
+
+/* Container for the table */
+.container {
+ padding: 12px;
+ border-radius: 4px;
+ background-color: var(--dark-bg-color) !important;
+}
+
+/* Styles for the table itself */
+.table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 1em;
+ text-align: left;
+ background-color: var(--table-dark-bg-color) !important;
+}
+
+/* Styles for the table header */
+.tableHeaderBgColor {
+ background-color: var(--table-header-bg-color) !important;
+}
+
+/* Styles for table rows */
+.tableOddRowsColor {
+ background-color: var(--table-odd-rows-color) !important;
+}
+
+.tableEvenRowsColor {
+ background-color: var(--table-even-rows-color) !important;
+}
+
+/* Text colors */
+.primaryTextColor {
+ color: var(--primary-text-color) !important;
+}
+
+/* Borders for the table */
+.table td, .table th {
+ border-bottom: 1px solid var(--table-border-color) !important;
+}
+
+.table tbody tr:last-child {
+ border-bottom: 2px solid var(--table-border-color) !important;
+}
+
+/* Hover effect */
+.table tbody tr:hover {
+ background-color: var(--table-row-hover-bg-color) !important;
+}
+
+/* Icon buttons */
+.iconButton {
+ color: var(--primary-text-color) !important;
+}
+
+/* Ensure icon buttons inherit colors properly */
+.MuiIconButton-root {
+ color: var(--primary-text-color) !important;
+}
+
+/* MUI Components Typography*/
+.tableDate {
+ font-size: var(--fs-300) !important;
+ color: var(--primary-text-color) !important;
+}
+
+.tableTotalScore {
+ font-size: var(--fs-300) !important;
+ color: var(--primary-text-color) !important;
+ margin-bottom: 0.8rem !important;
+ font-weight: 600 !important;
+}
+
+.tableExerciseData {
+ font-size: var(--fs-200);
+ color: var(--primary-text-color) !important;
+}
diff --git a/server/.gitignore b/server/.gitignore
index 5b450e0..7fd633b 100644
--- a/server/.gitignore
+++ b/server/.gitignore
@@ -4,3 +4,6 @@
/node_modules
/db/database.sqlite
/db/test_database.sqlite
+
+#Environment Configuration Files
+.env
\ No newline at end of file
diff --git a/server/controllers/workouts-controller.js b/server/controllers/workouts-controller.js
index 1d1a40c..bbc6edf 100644
--- a/server/controllers/workouts-controller.js
+++ b/server/controllers/workouts-controller.js
@@ -116,7 +116,6 @@ const addExercises = async (req, res) => {
const { id } = req.params; // Expecting workoutId in the URL parameters
const { exercises } = req.body; // Expecting workoutId and list of exerciseIds (with sets completed) in the request body
-
if (!id || !Array.isArray(exercises) || exercises.length === 0) {
return res.status(400).json({ error: 'Workout ID and a list of exercise IDs (with sets completed) are required.' });
}
@@ -153,7 +152,7 @@ const getExercises = async (req, res) => {
.join('exercises as e', 'e.id', 'we.exercise_id')
.where('we.workout_id', id)
.andWhere('we.user_id', req.session.user.user_id)
- .select('e.id', 'we.id', 'e.name', 'e.muscle_group', 'e.sets', 'e.weight', 'we.sets_completed');
+ .select('e.id', 'we.id', 'e.name', 'e.reps', 'e.setsGoal', 'e.weight', 'we.sets_completed');
if (exercises.length === 0) {
return res.status(404).json({ message: 'No exercises found for this workout.' });