From f7d7c9fcb08dffc367422ba7542869df6ae85d8d Mon Sep 17 00:00:00 2001 From: potauro Date: Thu, 12 Aug 2021 17:20:28 +0200 Subject: [PATCH] changes to profile and explore sections, big push --- .DS_Store | Bin 10244 -> 10244 bytes app.js | 5 +- models/Videos-model.js | 5 + olympi-client/package-lock.json | 34 +++ olympi-client/package.json | 7 +- olympi-client/src/App.js | 21 +- .../src/components/Explore/Explore.js | 21 ++ .../ExploreBottomNavBar.css | 1 + .../ExploreBottomNavBar.js | 39 ++- .../Explore/ExploreVideos/ExploreVideos.js | 93 ++++--- olympi-client/src/components/Explore/Fails.js | 11 +- .../src/components/Explore/Filters/Filters.js | 21 +- olympi-client/src/components/Explore/Learn.js | 12 +- .../Explore/SearchBar/SearchBar.css | 39 +-- .../components/Explore/SearchBar/SearchBar.js | 17 +- .../src/components/Explore/Trending.js | 19 -- .../Profile/BottomNavBar/BottomNavBar.css | 1 + .../Profile/PersonalBio/PersonalBio.css | 33 ++- .../Profile/PersonalBio/PersonalBio.js | 108 ++++++++- .../Profile/PersonalRecord/PersonalRecord.css | 20 ++ .../Profile/PersonalRecord/PersonalRecord.js | 126 +++++++--- .../Profile/ProfileVideos/ProfileVideos.css | 35 +++ .../Profile/ProfileVideos/ProfileVideos.js | 76 +++--- .../src/components/Profile/userProfile.js | 10 +- .../src/components/auth/auth-service.js | 3 +- .../src/components/feed/FeedFooter.js | 2 +- .../src/components/feed/feed-service.js | 1 - olympi-client/src/index.js | 5 +- public/.DS_Store | Bin 6148 -> 6148 bytes routes/auth-routes.js | 38 ++- routes/videos-routes.js | 229 ++++++++++-------- 31 files changed, 712 insertions(+), 320 deletions(-) create mode 100644 olympi-client/src/components/Explore/Explore.js delete mode 100644 olympi-client/src/components/Explore/Trending.js diff --git a/.DS_Store b/.DS_Store index 29a50093d884e22d0a006b1978590d555485e535..9cbe56bfe7a4f51002d9aa62ee90bacd53402990 100644 GIT binary patch delta 764 zcmZn(XbG6$&uG0dU^hRb^=2M{BaA8x3=D6(k`6lpX=a8bhD?UM-24=mq@4UDpa=&< zcJdd&EGDL+$q7PoP?5uqK#s`dSwdWFP$jE`mU)OA0dm+FavAa&QW#Phau|wp^Ig!) z0^5Y92Hn2RGlXNA1ai_1gOl@f3xH-aFvwauPF^Jl5EcNvQN&P!%|`6%FdcwR9g=%kfbs^D zSBZJ6tN=0DfL4|Q;~)nZ3(1()PL>pxgN3%OxB}yc$#LS|OdiW8uNIeqiJwI%`3O{! iFj+_<875aP0aLblu7nWVW_E>ltRoHy(FnN{GG7p~lKs{^>xeWOXDGaF$ISj?Q z`7Us?l)K?Jp{YUG2T`+mhHxy?mC$mYoGcB7w*L@?7ly4cFS0S9s6ki^Q8U?7Qh`xma+YMWMxPE`6Dva* qFkDg@%0b2=v_MpB{wgWNwy`0CaWlKZJ5~}L9ZfAqgX0dxV+8;@-Is*` diff --git a/app.js b/app.js index d759c77..b5987ef 100644 --- a/app.js +++ b/app.js @@ -1,5 +1,3 @@ - -const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost/olympi-server' require('dotenv').config(); const bodyParser = require('body-parser'); const cookieParser = require('cookie-parser'); @@ -16,6 +14,7 @@ const MongoStore = require('connect-mongo'); const app_name = require('./package.json').name; +const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost/olympi-server' mongoose .connect(MONGODB_URI, { useNewUrlParser: true, @@ -90,4 +89,4 @@ app.use((req, res, next) => { }); -module.exports = app; +module.exports = app; \ No newline at end of file diff --git a/models/Videos-model.js b/models/Videos-model.js index 0ed2996..4919840 100644 --- a/models/Videos-model.js +++ b/models/Videos-model.js @@ -22,6 +22,11 @@ const videoSchema = new Schema({ type: Number, required: true }, + votes: { + type: Number, + default: 0, + required: false + }, weight_metric: { type: String, enum: { diff --git a/olympi-client/package-lock.json b/olympi-client/package-lock.json index 6ae1a19..8731359 100644 --- a/olympi-client/package-lock.json +++ b/olympi-client/package-lock.json @@ -6490,6 +6490,11 @@ "strip-eof": "^1.0.0" } }, + "exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -10492,6 +10497,11 @@ "minimist": "^1.2.5" } }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -12788,6 +12798,22 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-modal": { + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.14.3.tgz", + "integrity": "sha512-+C2KODVKyu20zHXPJxfOOcf571L1u/EpFlH+oS/3YDn8rgVE51QZuxuuIwabJ8ZFnOEHaD+r6XNjqwtxZnXO0g==", + "requires": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + } + }, "react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -15393,6 +15419,14 @@ "makeerror": "1.0.x" } }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "watchpack": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", diff --git a/olympi-client/package.json b/olympi-client/package.json index ee27045..1b9078b 100644 --- a/olympi-client/package.json +++ b/olympi-client/package.json @@ -10,8 +10,10 @@ "@testing-library/user-event": "^12.8.3", "axios": "^0.21.1", "cloudinary-react": "^1.7.0", + "moment": "^2.29.1", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-modal": "^3.14.3", "react-router-dom": "^5.2.0", "react-router-hash-link": "^2.4.3", "react-scripts": "4.0.3", @@ -21,7 +23,7 @@ "web-vitals": "^1.1.2" }, "scripts": { - "start": "REACT_APP_APIURL=http://localhost:5000 react-scripts start", + "start": "REACT_APP_APIURL=http://localhost:5000 PORT=3000 react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" @@ -43,5 +45,6 @@ "last 1 firefox version", "last 1 safari version" ] - } + }, + "devDependencies": {} } diff --git a/olympi-client/src/App.js b/olympi-client/src/App.js index 9a59b2c..b6c3507 100644 --- a/olympi-client/src/App.js +++ b/olympi-client/src/App.js @@ -9,7 +9,7 @@ import Feed from './components/feed/Feed.js'; import authService from './components/auth/auth-service.js'; import ProProfile from './components/Profile/proProfile.js'; import UserProfile from './components/Profile/userProfile.js'; -import Trending from './components/Explore/Trending.js'; +import Explore from './components/Explore/Explore.js'; import Terms from './components/home/OtherPages/Terms/Terms'; import Team from './components/home/OtherPages/Team/Team'; import TagDefinitions from './components/home/OtherPages/TagDefinitions/TagDefinitions'; @@ -69,16 +69,27 @@ class App extends Component { {this.state.user ? : } + + {/* go back an check this Karina */} + ( + this.state.user && + )} /> {/* go back an check this Karina */} - {/* - {this.state.user.professional ? : } - */} + + {this.state.user?.professional ? : } + {/* go back an check this Karina */} ( - + + )} /> + {/* ( + )} /> + ( + + )} /> */} {/* go back an check this Karina */} diff --git a/olympi-client/src/components/Explore/Explore.js b/olympi-client/src/components/Explore/Explore.js new file mode 100644 index 0000000..39f65ad --- /dev/null +++ b/olympi-client/src/components/Explore/Explore.js @@ -0,0 +1,21 @@ +import React, { useState } from 'react' +import ExploreBottomNavBar from './ExploreBottomNavBar/ExploreBottomNavBar' +import Filters from './Filters/Filters' +import SearchBar from './SearchBar/SearchBar' +import ExploreVideos from './ExploreVideos/ExploreVideos' + +function Explore() { + const [tab, setTab] = useState('trending') + const [sortBy, changeSortBy] = useState('desc') + console.log({ tab }) + return ( +
+ + + + +
+ ) +} + +export default Explore diff --git a/olympi-client/src/components/Explore/ExploreBottomNavBar/ExploreBottomNavBar.css b/olympi-client/src/components/Explore/ExploreBottomNavBar/ExploreBottomNavBar.css index 5f6a592..17c7bf7 100644 --- a/olympi-client/src/components/Explore/ExploreBottomNavBar/ExploreBottomNavBar.css +++ b/olympi-client/src/components/Explore/ExploreBottomNavBar/ExploreBottomNavBar.css @@ -1,4 +1,5 @@ .ExploreBottomNavBarContainer{ + z-index: 1; display: flex; height: 70px; background-color: #fafafa; diff --git a/olympi-client/src/components/Explore/ExploreBottomNavBar/ExploreBottomNavBar.js b/olympi-client/src/components/Explore/ExploreBottomNavBar/ExploreBottomNavBar.js index bb01ca9..d2a7671 100644 --- a/olympi-client/src/components/Explore/ExploreBottomNavBar/ExploreBottomNavBar.js +++ b/olympi-client/src/components/Explore/ExploreBottomNavBar/ExploreBottomNavBar.js @@ -1,32 +1,25 @@ -import "./ExploreBottomNavBar.css" -import React from 'react' import SearchRoundedIcon from '@material-ui/icons/SearchRounded'; +import React from 'react'; +import { useHistory } from 'react-router-dom' +import "./ExploreBottomNavBar.css"; function ExploreBottomNavBar() { + const history = useHistory() return (
- -
- -Explore -
- -
- -Feed -
- -
- -Profile -
- - - - - - +
history.push('/explore')}> + + Explore +
+
history.push('/videos')}> + + Feed +
+
history.push('/user')}> + + Profile +
) } diff --git a/olympi-client/src/components/Explore/ExploreVideos/ExploreVideos.js b/olympi-client/src/components/Explore/ExploreVideos/ExploreVideos.js index 2dae73e..bebbe4b 100644 --- a/olympi-client/src/components/Explore/ExploreVideos/ExploreVideos.js +++ b/olympi-client/src/components/Explore/ExploreVideos/ExploreVideos.js @@ -1,42 +1,73 @@ -import React from "react"; import MoreIcon from "@material-ui/icons/More"; +import moment from 'moment'; +import React, { useEffect, useState } from "react"; import "./ExploreVideos.css"; +import axios from "axios" + +function ExploreVideos({ category, sortBy }) { + const [videos, setVideos] = useState(null) + const loadVideos = async () => { + const { data } = await axios.get(`http://localhost:5000/videos/explore`, { + params: { + category, + sortBy + } + } + ) + setVideos(data); + } + useEffect(() => { + loadVideos() + }, [category, sortBy]); + + const updateVotes = (video) => { + axios.post(`http://localhost:5000/videos/${video._id}/upvote`, { + votes: video.votes ? video.votes + 1 : 1, + }) + .then(({ data }) => { + const updatedVideos = videos.map((video) => { + if (video._id === data._id) { + return data + } + return video + } + ) + setVideos(updatedVideos) + }) + .catch((e) => console.log("error", e)) + } -function ExploreVideos() { return (
-
-
username 10 May 2021
- -
-
- 2.3k -
- -
- -
username 10 May 2021
- -
-
- 2.3k -
- -
+ { + console.log("videos", videos) -
username 10 May 2021
- -
-
- 2.3k + } +
+ {videos && videos.map((video, index) => { + const { creator_id: creator } = video + return ( +
+
{creator.username} {moment(video.createdAt).format('LLL')}
+ +
+
updateVotes(video)}> + upvote{video.votes ? video.votes : 0} +
+ +
- -
-
+ ) + })}
- - +
+ + ); } -export default ExploreVideos; +export default ExploreVideos; \ No newline at end of file diff --git a/olympi-client/src/components/Explore/Fails.js b/olympi-client/src/components/Explore/Fails.js index c246040..54a4ccf 100644 --- a/olympi-client/src/components/Explore/Fails.js +++ b/olympi-client/src/components/Explore/Fails.js @@ -1,10 +1,17 @@ import React from 'react' +import ExploreBottomNavBar from './ExploreBottomNavBar/ExploreBottomNavBar' +import Filters from './Filters/Filters' +import SearchBar from './SearchBar/SearchBar' +import ExploreVideos from './ExploreVideos/ExploreVideos' function Fails() { return (
- this is where you will see funny fail videos -
+ + + + +
) } diff --git a/olympi-client/src/components/Explore/Filters/Filters.js b/olympi-client/src/components/Explore/Filters/Filters.js index 5514a9e..a8449f0 100644 --- a/olympi-client/src/components/Explore/Filters/Filters.js +++ b/olympi-client/src/components/Explore/Filters/Filters.js @@ -1,22 +1,13 @@ import React from 'react' import "./Filters.css" -function Filters() { +function Filters({changeSortBy}) { return (
- -
Filer by :
- -
- - - - - - -
- - - +
Sort By:
+
+ + +
) } diff --git a/olympi-client/src/components/Explore/Learn.js b/olympi-client/src/components/Explore/Learn.js index c140af1..044799e 100644 --- a/olympi-client/src/components/Explore/Learn.js +++ b/olympi-client/src/components/Explore/Learn.js @@ -1,11 +1,17 @@ import React from 'react' +import ExploreBottomNavBar from './ExploreBottomNavBar/ExploreBottomNavBar' +import Filters from './Filters/Filters' +import SearchBar from './SearchBar/SearchBar' +import ExploreVideos from './ExploreVideos/ExploreVideos' function Learn() { return (
- This is where you will find Learn Videos -
+ + + + + ) } - export default Learn diff --git a/olympi-client/src/components/Explore/SearchBar/SearchBar.css b/olympi-client/src/components/Explore/SearchBar/SearchBar.css index ad58a7f..940d528 100644 --- a/olympi-client/src/components/Explore/SearchBar/SearchBar.css +++ b/olympi-client/src/components/Explore/SearchBar/SearchBar.css @@ -1,12 +1,11 @@ -.SearchBarContainer{ +.SearchBarContainer { background-color: #fafafa; - display: flex; + display: flex; flex-direction: column; height: 100%; align-items: center; } - .ExploreTitle { color: #e41b23; display: flex; @@ -20,18 +19,16 @@ width: 85%; } -.ExploreIcons{ -display: flex; -width: 16%; -justify-content: space-between; -align-items: center; +.ExploreIcons { + display: flex; + width: 16%; + justify-content: space-between; + align-items: center; } -.ExploreIcons img{ +.ExploreIcons img { width: 20px; - } - - +} .SearchBoxContainer { display: flex; @@ -40,10 +37,10 @@ align-items: center; border-radius: 5px; width: 90%; font-size: 12px; - color: #B3B3B3; + color: #b3b3b3; padding: 10px; margin-bottom: 10px; - box-shadow: -1px -1px 10px #B3B3B330; + box-shadow: -1px -1px 10px #b3b3b330; } .PagesContainer { @@ -61,13 +58,17 @@ align-items: center; } .Trending { - color: #e41b23; - border-bottom: solid 2px #e41b23; + color: #00000030; flex: 1; } - -.Learn { +.selected-tab { + border-bottom: solid 2px #e41b23; + color: #e41b23; +} +.tab-border { border-bottom: solid 2px #00000040; +} +.Learn { color: #00000030; flex: 1; -} \ No newline at end of file +} diff --git a/olympi-client/src/components/Explore/SearchBar/SearchBar.js b/olympi-client/src/components/Explore/SearchBar/SearchBar.js index 684daa9..871097e 100644 --- a/olympi-client/src/components/Explore/SearchBar/SearchBar.js +++ b/olympi-client/src/components/Explore/SearchBar/SearchBar.js @@ -3,17 +3,16 @@ import "./SearchBar.css" import SearchRoundedIcon from '@material-ui/icons/SearchRounded'; -function SearchBar() { +function SearchBar({ changeTab, tab }) { return (
- -
Explore
-
Search...
-
-
Fails
-
Trending
-
Learn
-
+
Explore
+
Search...
+
+
changeTab("fails")}>Fails
+
changeTab("trending")}>Trending
+
changeTab("learn")}>Learn
+
) diff --git a/olympi-client/src/components/Explore/Trending.js b/olympi-client/src/components/Explore/Trending.js deleted file mode 100644 index d1c471e..0000000 --- a/olympi-client/src/components/Explore/Trending.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import ExploreBottomNavBar from './ExploreBottomNavBar/ExploreBottomNavBar' -import Filters from './Filters/Filters' -import SearchBar from './SearchBar/SearchBar' -import ExploreVideos from './ExploreVideos/ExploreVideos' - -function Trending() { - return ( -
- - - - - -
- ) -} - -export default Trending diff --git a/olympi-client/src/components/Profile/BottomNavBar/BottomNavBar.css b/olympi-client/src/components/Profile/BottomNavBar/BottomNavBar.css index 1891813..a962b0e 100644 --- a/olympi-client/src/components/Profile/BottomNavBar/BottomNavBar.css +++ b/olympi-client/src/components/Profile/BottomNavBar/BottomNavBar.css @@ -1,4 +1,5 @@ .BottomNavBarContainer{ + z-index: 1; display: flex; height: 70px; background-color: #fafafa; diff --git a/olympi-client/src/components/Profile/PersonalBio/PersonalBio.css b/olympi-client/src/components/Profile/PersonalBio/PersonalBio.css index 0cab8c2..76d0c42 100644 --- a/olympi-client/src/components/Profile/PersonalBio/PersonalBio.css +++ b/olympi-client/src/components/Profile/PersonalBio/PersonalBio.css @@ -55,4 +55,35 @@ max-width: 30%; font-size: 12px; font-weight:500; margin-bottom: 10px; -} \ No newline at end of file +} +input[type=text], input[type=email], input[type=password] { + width: 100%; + padding: 15px; + margin: 5px 0 22px 0; + display: inline-block; + border: none; + background: #f1f1f1; +} +input:focus{ + border-color: #E51B23; +} +.EditPr:hover{ +cursor: pointer; +} +.ProeditImage:hover +{ + cursor: pointer; +} +.updateButton{ + + background-color: #E41E1E; + border: none; + border-radius: 10px; + color: #FDEFEF; + font-weight: 400; + font-size: 16px; + margin: 10px 5px 40px; + padding: 5px 15px; + +} + diff --git a/olympi-client/src/components/Profile/PersonalBio/PersonalBio.js b/olympi-client/src/components/Profile/PersonalBio/PersonalBio.js index 66c6e0c..0770cac 100644 --- a/olympi-client/src/components/Profile/PersonalBio/PersonalBio.js +++ b/olympi-client/src/components/Profile/PersonalBio/PersonalBio.js @@ -1,20 +1,110 @@ -import React from 'react' +import React, {useEffect, useState} from 'react' import "./PersonalBio.css" +import SportsIcon from '@material-ui/icons/Sports'; +import Modal from "react-modal" +import axios from "axios" +const baseUrl = process.env.REACT_APP_APIURL +const customStyles = { + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + width: "350px", + zIndex: 1 + }, +}; +function PersonalBio({user}) { + const [profile, setProfile] = useState({ + username:"", + email:"", + city:"", + fav_exercise:"", + about:"" + }) + Modal.setAppElement(document.getElementById("root")) + let subtitle; + const [modalIsOpen, setIsOpen] = useState(false); -function PersonalBio() { - return ( -
+ function openModal() { + setIsOpen(true); + } + + function afterOpenModal() { + // references are now sync'd and can be accessed. + subtitle.style.color = '#f00'; + } -
Profile
+ function closeModal() { + setIsOpen(false); + } + async function fetchUser(user) + { + await axios.get(`${baseUrl}/auth/user?id=${user}`) + .then((response) => { + if(response.status == 201) + { + + const data = response.data + setProfile({ + username: data.username, + email: data.email, + city: data.city, + fav_exercise: data.fav_exercise, + about: data.about + }) + } + }) + .catch((e) => console.log("Not Found", e)) + } + const handleChange = (event) => { + const {name, value} = event.target; + setProfile({...profile, [name]: value}); + } + const onUpdate = async() => { + const result = await axios.put(`${baseUrl}/auth/updateUser?id=${user._id}`, profile) + if(result.status == 201) + { + setProfile(result.data) + setIsOpen(false) + } + } + useEffect(() =>{ + fetchUser(user._id) + }, []) + return ( +
-
Edit
+
User Profile
+
Edit
+ +

(subtitle = _subtitle)}>Edit profile

+ {/* */} +
Edit your profile info
+
+

+

+

+

+

+ +
+
-
Karina Gonzalez
-
Paris, France 🇫🇷
-
I am a positive person and I love to exercise.
+
{profile.username}
+
{profile.city}
+
{profile.about}
+
diff --git a/olympi-client/src/components/Profile/PersonalRecord/PersonalRecord.css b/olympi-client/src/components/Profile/PersonalRecord/PersonalRecord.css index 6af337f..e9f4196 100644 --- a/olympi-client/src/components/Profile/PersonalRecord/PersonalRecord.css +++ b/olympi-client/src/components/Profile/PersonalRecord/PersonalRecord.css @@ -48,4 +48,24 @@ table { width: 90%; font-weight: 500; margin-left: 10px; +} +input[type=text], input[type=date], select { + width: 100%; + padding: 15px; + margin: 5px 0 22px 0; + display: inline-block; + border: none; + background: #f1f1f1; +} +.updateButton{ + + background-color: #E41E1E; + border: none; + border-radius: 10px; + color: #FDEFEF; + font-weight: 400; + font-size: 16px; + margin: 10px 5px 40px; + padding: 5px 15px; + } \ No newline at end of file diff --git a/olympi-client/src/components/Profile/PersonalRecord/PersonalRecord.js b/olympi-client/src/components/Profile/PersonalRecord/PersonalRecord.js index 43e91ec..a6828d5 100644 --- a/olympi-client/src/components/Profile/PersonalRecord/PersonalRecord.js +++ b/olympi-client/src/components/Profile/PersonalRecord/PersonalRecord.js @@ -1,13 +1,67 @@ -import React from 'react' +import React, {useState, useEffect} from 'react' import "./PersonalRecord.css" +import Modal from "react-modal" +import axios from "axios" +import moment from "moment" +const baseUrl = process.env.REACT_APP_APIURL +const customStyles = { + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + width: "350px", + zIndex: 1 + }, +}; +function PersonalRecord({user}) { + const [records, setRecords]= useState(null) + async function loadRecords(creator){ + const dd = await axios.get(`${baseUrl}/videos/loadUserVideos?creator=${creator}`) + .then((response) => { + setRecords(response.data); + }) + .catch((e) => console.log("not found", e)) + } +useEffect(() => { + user._id && loadRecords(user._id) + console.log("rcord", records) +}, []); + + + Modal.setAppElement(document.getElementById("root")) + let subtitle; + const [modalIsOpen, setIsOpen] = useState(false); + + function openModal() { + setIsOpen(true); + } + + function afterOpenModal() { + // references are now sync'd and can be accessed. + subtitle.style.color = '#f00'; + } + + function closeModal() { + setIsOpen(false); + } + + const EXERCISES = [ + 'Overhead press', + 'Deadlift', + 'Squat', + 'Bench press' + ] + -function PersonalRecord() { return (
Personal Record -
Edit +
Edit
@@ -21,40 +75,50 @@ function PersonalRecord() { - - Squat - 50 kg - 10 May 2021 - - - - - Deadlift - 50 kg - 10 May 2021 - + {records && records.map((record, index) => { + return + {record.exercise} + {`${record.weight} ${record.weight_metric}`} + {moment(record.createdAt).format("LL")} - - - - - Bench Press - 50 kg - 10 May 2021 - + })} - - - Overhead Press - 50 kg - 10 May 2021 - -
- + +

(subtitle = _subtitle)}>Personal Record

+ {/* */} + {/*
Edit your profile info
*/} +
+

+ +

+

+

+ +
+
diff --git a/olympi-client/src/components/Profile/ProfileVideos/ProfileVideos.css b/olympi-client/src/components/Profile/ProfileVideos/ProfileVideos.css index 65f9cea..1bed0a9 100644 --- a/olympi-client/src/components/Profile/ProfileVideos/ProfileVideos.css +++ b/olympi-client/src/components/Profile/ProfileVideos/ProfileVideos.css @@ -1,6 +1,7 @@ .ProfileVideosContainer{ display: flex; flex-direction: column; + margin: 5px; } @@ -24,4 +25,38 @@ .VideosSection{ margin-left: 35px; +} +.videoContainer{ + display: flex; + flex-direction: row; + max-width: 100%; + flex-wrap: wrap; +} +.video{ + position: relative; + margin: 10px; +} +.deleteVideoButton{ + margin-top: 45px !important; + margin-left: 174px; + position: absolute; + color: red; + z-index: 1; +} +.deleteVideoButton:hover{ + cursor: pointer; +} +@media (max-width:485px) +{ + .video{ + margin: auto; + } + .video video{ + width: 300px; + height: 300px; + } + .deleteVideoButton { + margin-top: 70px !important; + margin-left: 272px; + } } \ No newline at end of file diff --git a/olympi-client/src/components/Profile/ProfileVideos/ProfileVideos.js b/olympi-client/src/components/Profile/ProfileVideos/ProfileVideos.js index 9af7716..55b97a7 100644 --- a/olympi-client/src/components/Profile/ProfileVideos/ProfileVideos.js +++ b/olympi-client/src/components/Profile/ProfileVideos/ProfileVideos.js @@ -1,35 +1,55 @@ -import React from 'react' +import React, {useEffect, useState} from 'react' import "./ProfileVideos.css" -function ProfileVideos() { - return ( +import axios from "axios" +import HighlightOff from "@material-ui/icons/HighlightOff"; +const baseUrl = process.env.REACT_APP_APIURL + +function ProfileVideos({user}) { + const [videos, setVideos]= useState(null) + async function loadUserVideos(creator){ + const dd = await axios.get(`${baseUrl}/videos/loadUserVideos?creator=${creator}`) + .then((response) => { + setVideos(response.data); + }) + .catch((e) => console.log("not found", e)) + } + useEffect(() => { + user._id && loadUserVideos(user._id) + }, []); + + async function removeVideo(videoId) + { + const dd = await axios.delete(`${baseUrl}/videos/${videoId}`) + .then((response) => { + if(response.status == 204) + loadUserVideos(user._id) + }) + .catch((e) => console.log("Not Deleted", e)) + + } + return (
Videos -
Edit
- - -
- - - - - - - - - - - - -
- - - - - - - - + {/*
Edit
*/} +
+ + +
+
+ {videos && videos.map((video, index) => { + return
+ removeVideo(video._id)} className={"sdf"} /> + + +
+ })} +
+
diff --git a/olympi-client/src/components/Profile/userProfile.js b/olympi-client/src/components/Profile/userProfile.js index ad626bb..097e2eb 100644 --- a/olympi-client/src/components/Profile/userProfile.js +++ b/olympi-client/src/components/Profile/userProfile.js @@ -6,16 +6,14 @@ import ProfileVideos from './ProfileVideos/ProfileVideos' import SettingsBar from './SettingsBar/SettingsBar' import "./userProfile.css" -function UserProfile() { +function UserProfile({user}) { return (
- - - + + + - -
) } diff --git a/olympi-client/src/components/auth/auth-service.js b/olympi-client/src/components/auth/auth-service.js index 1eacec8..e8e6aed 100644 --- a/olympi-client/src/components/auth/auth-service.js +++ b/olympi-client/src/components/auth/auth-service.js @@ -1,6 +1,5 @@ // components/auth/auth-service.js import axios from 'axios'; - export default { service: axios.create({ @@ -15,7 +14,7 @@ export default { signup(data) { const {professional, username, email, password, city, fav_exercise, career_date, certifications, website, about} = data; - return this.service.post('/signup', { + return this.service.post(`/signup`, { professional, username, email, diff --git a/olympi-client/src/components/feed/FeedFooter.js b/olympi-client/src/components/feed/FeedFooter.js index 2a4cbf5..0d57901 100644 --- a/olympi-client/src/components/feed/FeedFooter.js +++ b/olympi-client/src/components/feed/FeedFooter.js @@ -23,7 +23,7 @@ function FunctionFooter(props) {
- Profile + Profile
diff --git a/olympi-client/src/components/feed/feed-service.js b/olympi-client/src/components/feed/feed-service.js index c34565b..3d2f0dc 100644 --- a/olympi-client/src/components/feed/feed-service.js +++ b/olympi-client/src/components/feed/feed-service.js @@ -1,5 +1,4 @@ import axios from 'axios'; - export default { service: axios.create({ baseURL: `${process.env.REACT_APP_APIURL || ""}/videos`, diff --git a/olympi-client/src/index.js b/olympi-client/src/index.js index 39a6f50..b8b5182 100644 --- a/olympi-client/src/index.js +++ b/olympi-client/src/index.js @@ -2,8 +2,11 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import {BrowserRouter as Router} from 'react-router-dom'; - +if (module.hot) { + module.hot.accept(); +} ReactDOM.render( + diff --git a/public/.DS_Store b/public/.DS_Store index a9e0b99cf04e428ddfd18a434522c877d7e16b83..211cf2e84a640556997028cc0f7ad1ba3f209de8 100644 GIT binary patch delta 138 zcmZoMXffE}!NkVE!0@&!X|f-a9E@4SRKx)hJ?uDnACrtwic3;XeiBd!Sy*ba39|(g r$q1#mB<18MF)%P73rkHl nVYXmm$e*0Ytj6)>2UK|;GqQ3dVUTj+&7vFvESuRm{_+C=PfIU@ diff --git a/routes/auth-routes.js b/routes/auth-routes.js index c843373..8b6af69 100644 --- a/routes/auth-routes.js +++ b/routes/auth-routes.js @@ -132,12 +132,12 @@ router.get("/loggedin", (req, res, next) => { //////////////////////////////////////////////////////////////////////// router.get('/user', (req, res, next)=> { - if (!req.session.currentUser) { - res.status(401).json({message: "You need to be logged in to edit your profile"}); - return; - } - - User.findById(req.session.currentUser._id) + // need to be changed + // if (!req.session.currentUser) { + // res.status(401).json({message: "You need to be logged in to edit your profile"}); + // return; + // } + User.findById(req.query.id) .then(userFromDB => { res.status(201).json(userFromDB) }) @@ -145,7 +145,31 @@ router.get('/user', (req, res, next)=> { res.status(500).json({message: "Something went wrong when enterin the user's profile"}); }) }) - +router.put('/updateUser', (req, res, next)=> { + // res.send(req.body) + // return; + // need to be changed + // if (!req.session.currentUser) { + // res.status(401).json({message: "You need to be logged in to edit your profile"}); + // return; + // } + User.findByIdAndUpdate( + req.query.id, + { + username: req.body.username, + email: req.body.email, + city: req.body.city, + fav_exercise: req.body.fav_exercise, + about: req.body.about + }, + { new: true }) + .then(userFromDB => { + res.status(201).json(userFromDB) + }) + .catch(error => { + res.status(500).json({message: 'Error while saving user into DB.'}) + });; +}) router.post('/user', (req, res, next) => { // Check user is logged in diff --git a/routes/videos-routes.js b/routes/videos-routes.js index f8d4cae..acf3581 100644 --- a/routes/videos-routes.js +++ b/routes/videos-routes.js @@ -6,32 +6,32 @@ const uploader = require('../configs/cloudinary.config.js'); const Comments = require('../models/Comments-model.js'); const Videos = require('../models/Videos-model.js'); const User = require('../models/Users-model.js'); -const {EXERCISES, FORM, OVERHEADPRESS, DEADLIFT, SQUAT, BENCHPRESS} = require('../constants.js') +const { EXERCISES, FORM, OVERHEADPRESS, DEADLIFT, SQUAT, BENCHPRESS } = require('../constants.js') // POST route => to create a new video videosRouter.post('/', uploader.single('file'), async (req, res, next) => { - const {exercise, description, category, weight, weight_metric, reps, rounds} = req.body; + const { exercise, description, category, weight, weight_metric, reps, rounds } = req.body; // Check user is logged in if (!req.user) { - res.status(401).json({message: "You need to be logged in to upload your video"}); + res.status(401).json({ message: "You need to be logged in to upload your video" }); return; }; // Check a file has been provided if (!req.file) { - res.status(400).json({message: "No file uploaded!"}); + res.status(400).json({ message: "No file uploaded!" }); return; }; - - if(!EXERCISES.includes(exercise)) { //check if exercise spelling is correct - res.status(400).json({message: "Exercise does not exist. Please select one of the exercises provided."}) + + if (!EXERCISES.includes(exercise)) { //check if exercise spelling is correct + res.status(400).json({ message: "Exercise does not exist. Please select one of the exercises provided." }) return; }; - if(!weight_metric==='kg' && !weight_metric==='lb') { //check if exercise spelling is correct - res.status(400).json({message: "Metric does not exist. Please select one of the metrics provided."}) + if (!weight_metric === 'kg' && !weight_metric === 'lb') { //check if exercise spelling is correct + res.status(400).json({ message: "Metric does not exist. Please select one of the metrics provided." }) return; }; try { @@ -50,7 +50,7 @@ videosRouter.post('/', uploader.single('file'), async (req, res, next) => { newVideo = await newVideo.populate('comments').execPopulate(); res.status(201).json(newVideo); - } catch (e){ + } catch (e) { res.status(500).json(err); } @@ -60,56 +60,56 @@ videosRouter.post('/', uploader.single('file'), async (req, res, next) => { // GET route ==> getting videos videosRouter.get('/', async (req, res, next) => { if (!req.user) { - res.status(401).json({message: "You need to be logged in to upload your video"}); + res.status(401).json({ message: "You need to be logged in to upload your video" }); return; } try { - let videos = await Videos.find({category: 'trending'}); //find all trending videos - + let videos = await Videos.find({ category: 'trending' }); //find all trending videos + await Promise.all(videos.map(async video => { video = await video.populate('creator_id').execPopulate(); //populate video creator video = await video.populate( { - path:'comments', - populate:[{ - path:'to_id', + path: 'comments', + populate: [{ + path: 'to_id', model: User }, { - path:'author_id', + path: 'author_id', model: User }] } - ).execPopulate(); //populate comments of the videos + ).execPopulate(); //populate comments of the videos })); - + videos = videos.sort((a, b) => b.createdAt - a.createdAt) //sort videos by date created res.status(200).json(videos); } catch (err) { res.status(500).json(err) - }; + }; }); //POST /videos/:id/tags -videosRouter.post('/:videoId/tags', (req, res, next)=>{ - const {tags, exercise} = req.body; +videosRouter.post('/:videoId/tags', (req, res, next) => { + const { tags, exercise } = req.body; const user = req.user; let formTagExist = false; - - if(!mongoose.Types.ObjectId.isValid(req.params.videoId)) { //check if video ID exists + + if (!mongoose.Types.ObjectId.isValid(req.params.videoId)) { //check if video ID exists res.status(400).json({ message: 'Specified id is not valid' }); return; }; if (!user) { //check if user is logged-in - res.status(401).json({message: "You need to be logged in to upload your video"}); + res.status(401).json({ message: "You need to be logged in to upload your video" }); return; }; if (!tags) { - res.status(400).json({message: "No tag selected!"}); + res.status(400).json({ message: "No tag selected!" }); return; }; @@ -117,18 +117,18 @@ videosRouter.post('/:videoId/tags', (req, res, next)=>{ if (!FORM.includes(tag)) formTagExist = true; }); - if(!EXERCISES.includes(exercise)) { //check if exercise spelling is correct - res.status(400).json({message: "Exercise does not exist. Please select one of the exercises provided."}) + if (!EXERCISES.includes(exercise)) { //check if exercise spelling is correct + res.status(400).json({ message: "Exercise does not exist. Please select one of the exercises provided." }) return; }; - + tags.map(tag => { //check if tags of user exist - if(!FORM.includes(tag) && - !OVERHEADPRESS.includes(tag) && - !DEADLIFT.includes(tag) && + if (!FORM.includes(tag) && + !OVERHEADPRESS.includes(tag) && + !DEADLIFT.includes(tag) && !SQUAT.includes(tag) && !BENCHPRESS.includes(tag)) { //check if form scoring tag is correct - res.status(400).json({message: "One of the tags does not exist. Please select only the tags provided."}) + res.status(400).json({ message: "One of the tags does not exist. Please select only the tags provided." }) return; }; }); @@ -136,7 +136,7 @@ videosRouter.post('/:videoId/tags', (req, res, next)=>{ Videos.findById(req.params.videoId) .then(videoFromDB => { //add userId to each tag which they chose tags.forEach(item => { - if(!videoFromDB.tags[item].includes(user._id)){ + if (!videoFromDB.tags[item].includes(user._id)) { videoFromDB.tags[item].push(user) } }) @@ -145,7 +145,7 @@ videosRouter.post('/:videoId/tags', (req, res, next)=>{ res.status(201).json(response) }) .catch(err => { - res.status(500).json({message: 'Error while saving the updated video into DB.'}); + res.status(500).json({ message: 'Error while saving the updated video into DB.' }); }) }) .catch(err => { @@ -154,43 +154,68 @@ videosRouter.post('/:videoId/tags', (req, res, next)=>{ }) -// GET /videos/explore -videosRouter.get('/explore', (req, res, next) => { - if (!req.user) { - res.status(401).json({message: "You need to be logged in to upload your video"}); - return; +//update votes field on videos +videosRouter.post('/:videoId/upvote', async (req, res, next) => { + const { votes } = req.body; + console.log(req.body) + console.log("$$$$$", req.params) + console.log("$$$$$", req.query) + try { + let video = await Videos.findById(req.params.videoId) + video.votes = votes + video.save() + res.status(200).json(video) + } + catch (err) { + res.status(500).json({ message: 'Error while saving the updated video into DB.' }); } +}) + - Videos.find() - .populate('creator_id') - .populate('comments') - .then(videosFromDB => { - - const videosByCategory = { - trending: [], - fail: [], - learn: [] - } - - videosFromDB.forEach(video => videosByCategory[video.category].push(video)) - res.status(200).json(videosByCategory); - }) - .catch(err => { - res.status(500).json(err); - }) -}); +// GET /videos/explore +videosRouter.get('/explore', async (req, res, next) => { + // res.send(req.query) + // return; + // if (!req.user) { + // res.status(401).json({message: "You need to be logged in to upload your video"}); + // return; + // } + // const {category = 'trending', tags =[]} = req.query; + try { + const { category, sortBy } = req.query; + const allVideos = await Videos.find({ category: category }).sort({ votes: sortBy === 'desc' ? -1 : 1}) + .populate('creator_id') + .populate('comments') + +res.status(200).json(allVideos) + } + catch (error) { + res.status(500).json(error); +} +}); +videosRouter.get("/loadUserVideos", async (req, res, next) =>{ + // res.send("dta from server") + // return; + try{ + const userVideos = await Videos.find({creator_id: req.query.creator}) + res.status(200).json(userVideos) + } + catch(error){ + res.status(500).json(error); + } +}) // GET /videos/ask videosRouter.get('/ask', async (req, res, next) => { if (!req.user) { - res.status(401).json({message: "You need to be logged in to send a request"}); + res.status(401).json({ message: "You need to be logged in to send a request" }); return; } try { - let professionals = await User.find({professional: true}); //find all professional users - + let professionals = await User.find({ professional: true }); //find all professional users + //NO NEED TO POPULATE THE LIKES OF EACH PROFESSIONAL USER // await Promise.all(professionals.map(async professional => { // professional = await professional.populate('likes').execPopulate();; //populate comments of the videos @@ -198,118 +223,118 @@ videosRouter.get('/ask', async (req, res, next) => { res.status(200).json(professionals); } catch (err) { res.status(500).json(err) - }; + }; }); // POST /videos/:videoId/ask -videosRouter.post('/:videoId/ask', async (req, res, next)=>{ - const {question, to_id} = req.body; +videosRouter.post('/:videoId/ask', async (req, res, next) => { + const { question, to_id } = req.body; console.log('ask video id', req.params.videoId) - + if (!question || !to_id) { - res.status(400).json({message: "Please indicate the professional and a question!"}); + res.status(400).json({ message: "Please indicate the professional and a question!" }); return; }; - if(!mongoose.Types.ObjectId.isValid(req.params.videoId)) { //check if video ID exists + if (!mongoose.Types.ObjectId.isValid(req.params.videoId)) { //check if video ID exists res.status(400).json({ message: 'Specified video id is not valid' }); return; }; - if(!mongoose.Types.ObjectId.isValid(to_id)) { //check if professional ID exists + if (!mongoose.Types.ObjectId.isValid(to_id)) { //check if professional ID exists res.status(400).json({ message: 'Specified professional id is not valid' }); return; }; if (!req.user) { //check if user is logged-in - res.status(401).json({message: "You need to be logged in to upload your video"}); + res.status(401).json({ message: "You need to be logged in to upload your video" }); return; }; - + try { let newComment = await Comments.create({ author_id: req.user._id, question, to_id, }) - + let video = await Videos.findById(req.params.videoId); video.comments.push(newComment); video = await video.save(); - + newComment = await newComment.populate('author_id').execPopulate(); newComment = await newComment.populate('to_id').execPopulate(); res.status(201).json(newComment); - }catch{ + } catch { res.status(500).json(err); - }; - + }; + }) // POST /videos/:videoId/reply -videosRouter.post('/:videoId/reply', async (req, res, next)=>{ - const {reply, to_id, author_id, question} = req.body; +videosRouter.post('/:videoId/reply', async (req, res, next) => { + const { reply, to_id, author_id, question } = req.body; console.log('answer video id', req.params.videoId) - + if (!reply || !author_id || !question || !to_id) { - res.status(400).json({message: "Please indicate your answer, the question and the person asking!"}); + res.status(400).json({ message: "Please indicate your answer, the question and the person asking!" }); return; }; - if(!mongoose.Types.ObjectId.isValid(req.params.videoId)) { //check if video ID exists + if (!mongoose.Types.ObjectId.isValid(req.params.videoId)) { //check if video ID exists res.status(400).json({ message: 'Specified video id is not valid' }); return; }; - if(!mongoose.Types.ObjectId.isValid(author_id)) { //check if professional ID exists + if (!mongoose.Types.ObjectId.isValid(author_id)) { //check if professional ID exists res.status(400).json({ message: 'Specified user id is not valid' }); return; }; - if(!mongoose.Types.ObjectId.isValid(to_id)) { //check if professional ID exists + if (!mongoose.Types.ObjectId.isValid(to_id)) { //check if professional ID exists res.status(400).json({ message: 'Specified professional id is not valid' }); return; }; if (!req.user) { //check if user is logged-in - res.status(401).json({message: "You need to be logged in to upload your video"}); + res.status(401).json({ message: "You need to be logged in to upload your video" }); return; }; - + try { - let comment = await Comments.findOneAndUpdate({$and:[{author_id},{question}, {to_id}]}, {reply}) - + let comment = await Comments.findOneAndUpdate({ $and: [{ author_id }, { question }, { to_id }] }, { reply }) + comment = await comment.populate('author_id').execPopulate(); comment = await comment.populate('to_id').execPopulate(); res.status(201).json(comment); - }catch{ + } catch { res.status(500).json(err); - }; - + }; + }) //DELETE /videos/:id -videosRouter.delete('/:videoId', (req, res, next)=>{ +videosRouter.delete('/:videoId', (req, res, next) => { - if(!mongoose.Types.ObjectId.isValid(req.params.videoId)) { + if (!mongoose.Types.ObjectId.isValid(req.params.videoId)) { res.status(400).json({ message: 'Specified id is not valid' }); return; } - if (!req.user) { - res.status(401).json({message: "You need to be logged in to upload your video"}); - return; - } + // if (!req.user) { + // res.status(401).json({message: "You need to be logged in to upload your video"}); + // return; + // } Videos.findByIdAndRemove(req.params.videoId) .then(() => { res.status(204).json({ message: `Video with ${req.params.id} is removed successfully.` }); }) - .catch( err => { + .catch(err => { res.json(err); }) }) @@ -317,17 +342,17 @@ videosRouter.delete('/:videoId', (req, res, next)=>{ // GET route => to retrieve a specific video videosRouter.get('/:videoId', (req, res, next) => { if (!req.user) { - res.status(401).json({message: "You need to be logged in to upload your video"}); + res.status(401).json({ message: "You need to be logged in to upload your video" }); return; } - + Videos.findById(req.params.videoId) - .then(video =>{ + .then(video => { res.status(200).json(video); - }) - .catch( err =>{ + }) + .catch(err => { res.status(500).json(err); - }) + }) });