diff --git a/backend/models/user.js b/backend/models/user.js index d9af263..b07e1fc 100644 --- a/backend/models/user.js +++ b/backend/models/user.js @@ -41,6 +41,25 @@ const userSchema = new Schema( return this.fname + " " + this.lname; }, }, + _friends_: { + async get() { + await this.populate("friends", "username email"); + // console.log(this.friends); + return this.friends.map((friend) => { + return { + username: friend.username, + email: friend.email, + id: friend.id, + }; + }); + }, + }, + _games_: { + async get() { + await this.populate("games"); + return this.games; + }, + }, }, methods: { async getFriends() { diff --git a/backend/routes/user.js b/backend/routes/user.js index b2042f4..5a109d9 100644 --- a/backend/routes/user.js +++ b/backend/routes/user.js @@ -11,7 +11,6 @@ router.use( catchAsync(async (req, res, next) => { let userID = req.url.split("/")[1]; let userData = await User.findById(userID); - console.log(userData); if (userData) { req.userData = userData; next(); @@ -28,8 +27,19 @@ router.get( checkAuth, catchAsync(async (req, res, next) => { let userData = req.userData; - let { id, username, email, friends, fullName, country, location } = userData; - let resData = { id, username, email, friends, fullName, country, location }; + let { + id, + username, + email, + fname, + lname, + country, + location, + } = userData; + let friends = await userData.getFriends(); + let games = await userData.getGames(); + let resData = { id, username, email, friends, fname, lname, country, location, games }; + console.log(resData) return res.json({ success: true, data: resData }); }) ); @@ -42,6 +52,7 @@ router.patch( catchAsync(async (req, res, next) => { let { userid } = req.params; let updatedData = req.body; + console.log('Updated data: ',updatedData) // console.log(updatedData) await User.findByIdAndUpdate(userid, { ...updatedData }); let { id, username, email, fname, lname, location, country, fullName } = await User.findById(userid); diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index c50d70f..0217a02 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -16,6 +16,7 @@ import { logoutAction } from './components/Logout' import ChallengeFriend, { playFriendAction } from './pages/Play/ChallengeFriend' import ChessGame from './pages/Chess/ChessGame' import ChessGameContextProvider from './context/chess-game-context' +import { action as profileAction } from './pages/Settings/Profile' const router = createBrowserRouter([{ path: '/', @@ -43,8 +44,8 @@ const router = createBrowserRouter([{ }, { path: 'settings', element: , children: [ - { index: true, element: }, - { path: 'profile', element: }, + { index: true, element: , action: profileAction }, + { path: 'profile', element: , action: profileAction }, { path: 'themes', element: }, { path: 'password', element: }, { path: 'friends', element: }, diff --git a/frontend/src/context/user-data-context.jsx b/frontend/src/context/user-data-context.jsx new file mode 100644 index 0000000..f7dda22 --- /dev/null +++ b/frontend/src/context/user-data-context.jsx @@ -0,0 +1,45 @@ +import React, { createContext, useEffect, useState } from 'react' +import { getAuthToken, getUserData } from '../../utils/auth'; +export const UserDataContext = createContext(); + + +const UserDataContextProvider = ({ children }) => { + let { id: userid } = getUserData(); + const [user, setUser] = useState(null); + const [friends, setFriends] = useState(null) + const [games, setGames] = useState(null); + + async function fetchUserDetails() { + let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/${userid}`; + let response = await fetch(url, { + headers: { + Authorization: `Bearer ${getAuthToken()}` + } + }); + let resData = await response.json(); + if (resData.success) { + let { id, username, email, friends: _friends_, fname, lname, fullName, country, location, games: _games_ } = resData.data; + setUser({ id, username, email, fname, lname, fullName, country, location }) + setFriends(_friends_) + setGames(_games_) + localStorage.setItem('user', JSON.stringify({ id, username, email, fname, lname, fullName, country, location })); + localStorage.setItem('friends', JSON.stringify(_friends_)); + localStorage.setItem('games', JSON.stringify(_games_)); + } else { + throw resData.error + } + } + + useEffect(() => { + fetchUserDetails() + }, []); + + return ( + + {children} + + ) +} + + +export default UserDataContextProvider \ No newline at end of file diff --git a/frontend/src/layout/MainLayout.jsx b/frontend/src/layout/MainLayout.jsx index 3efc992..9e42dc7 100644 --- a/frontend/src/layout/MainLayout.jsx +++ b/frontend/src/layout/MainLayout.jsx @@ -1,23 +1,24 @@ -import { AppShell, Burger, Button, Container, Header, MediaQuery, Navbar, Paper, Text, createStyles, useMantineTheme } from '@mantine/core' +import { AppShell, Burger, Container, Header, MediaQuery, Navbar, Paper, Text, createStyles, useMantineTheme } from '@mantine/core' import React, { useState } from 'react' import NavbarLinks from '../components/NavbarLinks'; import { Outlet } from 'react-router-dom' import Logout from '../components/Logout'; +import UserDataContextProvider from '../context/user-data-context'; const useStyles = createStyles((theme) => ({ body: { width: '100%', - height:'100%' + height: '100%' }, root: { width: '100%', - height:'100vh' + height: '100vh' }, main: { width: '100%', - height:'100vh' + height: '100vh' } })) @@ -26,57 +27,59 @@ const MainLayout = () => { const theme = useMantineTheme(); const [opened, setOpened] = useState(false); return ( - - + + + + ); } diff --git a/frontend/src/pages/Settings/Profile.jsx b/frontend/src/pages/Settings/Profile.jsx index c7a28a0..9cd54a4 100644 --- a/frontend/src/pages/Settings/Profile.jsx +++ b/frontend/src/pages/Settings/Profile.jsx @@ -1,9 +1,26 @@ -import React from 'react' -import { Avatar, Card, Container, Flex, Grid, Group, Image, Stack, Text, TextInput, Title } from '@mantine/core' -import { getUserData } from '../../../utils/auth' +import React, { useContext } from 'react' +import { Avatar, Button, Flex, Grid, Group, Loader, Stack, Text, TextInput, Title } from '@mantine/core' +import { getAuthToken, getUserData } from '../../../utils/auth' +import { UserDataContext } from '../../context/user-data-context' +import { useForm } from '@mantine/form' +import { Form } from 'react-router-dom' const Profile = () => { - let { username } = getUserData() + let { user } = useContext(UserDataContext); + + if(!user) { + return + } + + let { username, email, fname, lname, country, location } = user; + const form = useForm({ + initialValues: { + username, fname, email, lname, country, location + }, + }) + + let fullName = (fname && lname) ? fname + ' ' + lname : null; + return ( @@ -15,29 +32,66 @@ const Profile = () => {
{username} - {'-------'} - city + {fullName || '-------'}, + {location || '-----'}
- - - - - - - - - - - - - - - - +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
) } +export const action = async ({ request }) => { + const data = await request.formData(); + let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/${getUserData().id}` + + const reqBody = { + fname: data.get('fname'), lname: data.get('lname'), country: data.get('country'), location: data.get('location') + } + console.log(reqBody) + + const response = await fetch(url, { + body: JSON.stringify(reqBody), + method: 'PATCH', + headers: { + Authorization: `Bearer ${getAuthToken()}`, + 'Content-Type':'application/json' + } + }) + const resData = await response.json(); + console.log(resData) + if (!resData.success) { + return resData.error; + } else return null; +} + + export default Profile \ No newline at end of file diff --git a/frontend/src/pages/Settings/Settings.jsx b/frontend/src/pages/Settings/Settings.jsx index e4358d8..a81b6f4 100644 --- a/frontend/src/pages/Settings/Settings.jsx +++ b/frontend/src/pages/Settings/Settings.jsx @@ -1,5 +1,5 @@ -import { Box, Flex, NavLink, Stack, Text, ThemeIcon, Title } from '@mantine/core' -import { ColorWheelIcon, GearIcon, GlobeIcon, LockOpen1Icon, LockOpen2Icon, PersonIcon } from '@radix-ui/react-icons' +import { Box, Flex, NavLink, Title } from '@mantine/core' +import { ColorWheelIcon, GearIcon, GlobeIcon, LockOpen1Icon, PersonIcon } from '@radix-ui/react-icons' import React, { useState } from 'react' import { Link, Outlet } from 'react-router-dom';