diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..2b7bafa --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-react"] +} diff --git a/.env b/.env new file mode 100644 index 0000000..17f4aad --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +NODE_ENV=development + +API_KEY="SReYt5aEyGMKzrdSe87wew==boZAObqiLCiQPGrb" + +MONGO_URI="mongodb+srv://nlspglenn:TwTpQOuWG9VYoeSh@cluster0.k6qxbtg.mongodb.net/?retryWrites=true&w=majority" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8ece3ba..d1cb904 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ node_modules package-lock.json build -.env \ No newline at end of file diff --git a/Client/App.jsx b/Client/App.jsx index e2b3b8f..5b57806 100644 --- a/Client/App.jsx +++ b/Client/App.jsx @@ -1,15 +1,31 @@ +/** + * ************************************ + * + * @module + * @author Eivind Del Fierro, Morah Geist + * @date 07/2023 + * @description main app container rendering 3 child containers + * + * ************************************ + */ + import React from 'react'; -import MainContainer from './containers/MainContainer.jsx'; import HeaderContainer from './containers/HeaderContainer.jsx'; +import TimerContainer from './containers/TimerContainer.jsx'; +import MenuContainer from './containers/MenuContainer.jsx'; +import StretchContainer from './containers/StretchContainer.jsx'; +import './stylesheets/application.scss'; // Init func app that returns our main containers const App = () => { return ( -
+
- + + +
); }; -export default App \ No newline at end of file +export default App; diff --git a/Client/actionCreator/actionCreator.js b/Client/actionCreator/actionCreator.js new file mode 100644 index 0000000..6b7642c --- /dev/null +++ b/Client/actionCreator/actionCreator.js @@ -0,0 +1,30 @@ +import * as types from '../constant/actionTypes'; + +export const updateExercisesFromAPI = (array) => ({ + type: types.UPDATE_FROM_API, + payload: array, +}); + +export const updateUSER_LOG_ON = (userObj) => ({ + type: types.USER_LOG_ON, + payload: userObj, +}); + +export const updateUSER_LOG_OFF = () => ({ + type: types.USER_LOG_OFF, +}); + +export const updateDifficultyAndMuscle = ([muscle, difficulty]) => ({ + type: types.UPDATE_MUSCLE_DIFFICULTY, + payload: [muscle, difficulty] +}) + +export const updateADD_FAVORITE = (obj) => ({ + type: types.ADD_FAVORITE, + payload: obj +}) + +export const updateREMOVE_FAVORITE = (obj) => ({ + type: types.REMOVE_FAVORITE, + payload: obj +}) \ No newline at end of file diff --git a/Client/components/LabeledText.jsx b/Client/components/LabeledText.jsx new file mode 100644 index 0000000..5ed46bb --- /dev/null +++ b/Client/components/LabeledText.jsx @@ -0,0 +1,22 @@ +/** + * ************************************ + * + * @module LabeledText + * @author + * @date + * @description Simple presentation component that shows a bold label next to plain text + * + * ************************************ + */ + +import React from 'react'; +import '../stylesheets/application.scss'; + +const LabeledText = ({ label, text }) => ( +

+ {`${label}: `} + {text} +

+); + +export default LabeledText; diff --git a/Client/components/Stretch.jsx b/Client/components/Stretch.jsx index ea1478e..a5175bd 100644 --- a/Client/components/Stretch.jsx +++ b/Client/components/Stretch.jsx @@ -1,20 +1,65 @@ +/** + * ************************************ + * + * @module Stretch + * @author Eivind Del Fierro, Morah Geist + * @date 07/2023 + * @description stretch box + * + * ************************************ + */ + import React from 'react'; +import { Link } from 'react-router-dom'; +import LabeledText from './LabeledText.jsx'; +import { FontAwesomeIcon as FAIcon } from '@fortawesome/react-fontawesome'; +import { faStar as solidStar } from '@fortawesome/free-solid-svg-icons'; +import { faStar as regStar } from '@fortawesome/free-regular-svg-icons'; +import { useDispatch, useSelector} from 'react-redux' +import * as actions from '../actionCreator/actionCreator.js' const Stretch = (props) => { + const thisExercise = props.exercises; + const dispatch = useDispatch(); + const state = useSelector( state => state.stretch) // insert any logic for the Stretch here + let FavIcon; + + const favClicked = () => { + + const userFavorites = state.favorites + let found = false + + userFavorites.forEach ( exercise => { + if (exercise == thisExercise) { + found = true + } + }); + + return (found) ? dispatch(actions.updateREMOVE_FAVORITE(thisExercise)) : dispatch(actions.updateADD_FAVORITE(thisExercise)); + // get the element property from the event + // add the exercise object to the favorites array + } + // return stretch component with passed-in props from query to server return ( -
-

{props.name}

+
+
+

{thisExercise.name}

+ {/* need logic to make Fav Icon then comment this back in and delete other */} + + + +
  • - Equipment: {props.equipment} +
  • - Difficulty: {props.difficulty} +
  • - Instructions: {props.instructions} +
diff --git a/Client/components/login.jsx b/Client/components/login.jsx new file mode 100644 index 0000000..14f71be --- /dev/null +++ b/Client/components/login.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { useDispatch } from 'react-redux'; +import * as actions from '../actionCreator/actionCreator.js'; + +const login = () => { + const dispatch = useDispatch(); + + const loginFunc = (event) => { + event.preventDefault(); + const un = document.getElementById('usernameLogin').value; + const pw = document.getElementById('passwordLogin').value; + + const loginObj = { + username: un, + password: pw, + }; + + fetch('http://localhost:3000/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(loginObj), + }) + .then((data) => data.json()) + .then((data) => { + if (!data.err) { + console.log(data); + // Do something + // change logged in user state with the returned userState + dispatch(actions.updateUSER_LOG_ON(data)); + + // disable opacity + const overlay = document.getElementById('overlay'); + overlay.style.opacity = 0; + setTimeout(() => overlay.style.display = 'none', 1000); + } else { + alert(data.err); + } + }) + .catch((error) => alert('Invalid Username/Password')); + }; + + return ( +
+
+ + + +
+
+ ); +}; + +export default login; diff --git a/Client/components/signup.jsx b/Client/components/signup.jsx new file mode 100644 index 0000000..66a5145 --- /dev/null +++ b/Client/components/signup.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { useDispatch } from 'react-redux'; +import * as actions from '../actionCreator/actionCreator.js'; + +const signup = () => { + const dispatch = useDispatch(); + + const signupFunc = (event) => { + event.preventDefault(); + const username = document.getElementById('usernameSignup').value; + const password1 = document.getElementById('passwordSignup').value; + const password2 = document.getElementById('passwordSignupConfirm').value; + + // check if passwords match + if (password1 !== password2) { + return alert('Sign up passwords do not match'); + } + + fetch('http://localhost:3000/user', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, password: password1 }), + }) + .then((data) => data.json()) + .then((data) => { + // if there is an error object + if (data.err) alert(data.err); + // update logged in user + dispatch(actions.updateUSER_LOG_ON(data)); + // disable opacity + const overlay = document.getElementById('overlay'); + overlay.style.opacity = 0; + setTimeout(() => overlay.style.display = 'none', 1000); + }) + .catch((error) => alert(error)); + }; + + return ( +
+
+ + + + +
+
+ ); +}; + +export default signup; diff --git a/Client/components/star-style.module.css b/Client/components/star-style.module.css new file mode 100644 index 0000000..5f8ab1b --- /dev/null +++ b/Client/components/star-style.module.css @@ -0,0 +1,14 @@ +.star_true { + /* represents on state */ + color: #d4af37; +} + +.star_false { + /* represents off state */ + color: black; +} + +.star-button { + background: none; + border: none; +} diff --git a/Client/constant/actionTypes.js b/Client/constant/actionTypes.js new file mode 100644 index 0000000..7961ba1 --- /dev/null +++ b/Client/constant/actionTypes.js @@ -0,0 +1,17 @@ +/** + * ************************************ + * + * @module + * @author Eivind Del Fierro, Morah Geist + * @date 07/2023 + * @description actions + * + * ************************************ + */ + +export const UPDATE_FROM_API = "UPDATE_FROM_API" +export const USER_LOG_ON = "USER_LOG_ON" +export const USER_LOG_OFF = "USER_LOG_OFF" +export const UPDATE_MUSCLE_DIFFICULTY = "UPDATE_MUSCLE_DIFFICULTY" +export const ADD_FAVORITE = "ADD_FAVORITE" +export const REMOVE_FAVORITE = "REMOVE_FAVORITE" \ No newline at end of file diff --git a/Client/containers/HeaderContainer.jsx b/Client/containers/HeaderContainer.jsx index 022472b..d90d546 100644 --- a/Client/containers/HeaderContainer.jsx +++ b/Client/containers/HeaderContainer.jsx @@ -1,24 +1,34 @@ -import React from 'react'; -import '../styles.css'; +/** + * ************************************ + * + * @module Header + * @author Eivind Del Fierro, Morah Geist + * @date 07/2023 + * @description header feature on main page of app + * + * ************************************ + */ -{ - /* This is the HeaderContainer in Client/containers/HeaderContainer.jsx */ -} +import React from 'react'; +import * as actions from '../actionCreator/actionCreator.js'; +import { useDispatch } from 'react-redux'; const HeaderContainer = () => { - // insert any logic for the HeaderContainer here + const dispatch = useDispatch(); + + const logoutHandler = () => { + dispatch(actions.updateUSER_LOG_OFF()); + const overlay = document.getElementById('overlay'); + overlay.style.opacity = 1; + overlay.style.display = 'block' + }; + return ( -