updated user functionality
This commit is contained in:
parent
01adc806d7
commit
0fa054e096
9 changed files with 128 additions and 22 deletions
|
@ -2,15 +2,18 @@ import {useSelector} from'react-redux'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import {getDownloadURL, getStorage, ref, uploadBytesResumable} from 'firebase/storage'
|
import {getDownloadURL, getStorage, ref, uploadBytesResumable} from 'firebase/storage'
|
||||||
import { app } from '../firebase';
|
import { app } from '../firebase';
|
||||||
|
import {updateUserStart, updateUserSuccess, updateUserFailure} from '../redux/user/userSlice.js'
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
export const Profile = () => {
|
export const Profile = () => {
|
||||||
const fileRef = useRef(null);
|
const fileRef = useRef(null);
|
||||||
const {currentUser} = useSelector((state) => state.user);
|
const {currentUser, loading, error} = useSelector((state) => state.user);
|
||||||
const [file, setFile] = useState(undefined);
|
const [file, setFile] = useState(undefined);
|
||||||
const [filePercentage, setFilePercentage] = useState(0);
|
const [filePercentage, setFilePercentage] = useState(0);
|
||||||
const [fileUploadError, setFileUploadError] = useState(false)
|
const [fileUploadError, setFileUploadError] = useState(false)
|
||||||
const [formData, setFormData] = useState({})
|
const [formData, setFormData] = useState({})
|
||||||
|
const [updateSuccess, setUpdateSuccess] = useState(false);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
|
||||||
useEffect (() => {
|
useEffect (() => {
|
||||||
|
@ -39,14 +42,40 @@ export const Profile = () => {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
const handleChange = (e) => {
|
||||||
|
setFormData({...formData, [e.target.id]: e.target.value})
|
||||||
|
}
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
dispatch(updateUserStart());
|
||||||
|
const res = await fetch(`/server/user/update/${currentUser._id}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(formData)
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if(data.success === false){
|
||||||
|
dispatch(updateUserFailure(data.message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(updateUserSuccess(data));
|
||||||
|
setUpdateSuccess(true);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(updateUserFailure(error.message));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='max-w-lg p-3 mx-auto'>
|
<div className='max-w-lg p-3 mx-auto'>
|
||||||
<h1 className="text-2xl uppercase text-center text-blue-900 font-serif my-10 tracking-wide">Profile</h1>
|
<h1 className="text-2xl uppercase text-center text-blue-900 font-serif my-10 tracking-wide">Profile</h1>
|
||||||
<form className='flex flex-col gap-4'>
|
<form onSubmit={handleSubmit} className='flex flex-col gap-4'>
|
||||||
<input onChange={(e) => setFile(e.target.files[0])} type="file" ref={fileRef} hidden accept='image/*'/>
|
<input onChange={(e) => setFile(e.target.files[0])} type="file" ref={fileRef} hidden accept='image/*'/>
|
||||||
<img onClick={() => fileRef.current.click()} src={formData.avatar || currentUser.avatar} alt="" className='rounded-full h-24 w-24 object-cover self-center mt-2 cursor-pointer bg-blue-100'/>
|
<img onClick={() => fileRef.current.click()} src={formData?.avatar || currentUser.avatar} alt="" className='rounded-full h-24 w-24 object-cover self-center mt-2 cursor-pointer bg-blue-100'/>
|
||||||
<p className='text-sm self-center'>
|
<p className='text-sm self-center'>
|
||||||
{
|
{
|
||||||
fileUploadError ? (<span className='text-red-700'>Error image upload (image must be less than 2 mb) </span>) :
|
fileUploadError ? (<span className='text-red-700'>Error image upload (image must be less than 2 mb) </span>) :
|
||||||
|
@ -54,15 +83,17 @@ export const Profile = () => {
|
||||||
filePercentage === 100 ? (<span className='text-green-900'>Image successfully uploaded</span>) : ("")
|
filePercentage === 100 ? (<span className='text-green-900'>Image successfully uploaded</span>) : ("")
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
<input type="text" placeholder='Username' id='username' className='border p-2 rounded-md text-sm'/>
|
<input type="text" placeholder='Username' id='username' defaultValue={currentUser.username} onChange={handleChange} className='border p-2 rounded-md text-sm'/>
|
||||||
<input type="email" placeholder='Email' id='email' className='border p-2 rounded-md text-sm'/>
|
<input type="email" placeholder='Email' id='email' defaultValue={currentUser.email} onChange={handleChange} className='border p-2 rounded-md text-sm'/>
|
||||||
<input type="password" placeholder='Password' id='password' className='border p-2 rounded-md text-sm'/>
|
<input type="password" placeholder='Password' id='password' onChange={handleChange} className='border p-2 rounded-md text-sm'/>
|
||||||
<button className='bg-blue-950 text-white text-sm p-2 rounded-md uppercase hover:opacity-90 disabled:opacity-80'>Update</button>
|
<button disabled={loading} className='bg-blue-950 text-white text-sm p-2 rounded-md uppercase hover:opacity-90 disabled:opacity-80'>{loading ? 'Loading...' : 'Update'}</button>
|
||||||
</form>
|
</form>
|
||||||
<div className='flex justify-between mt-4'>
|
<div className='flex justify-between mt-4'>
|
||||||
<span className='text-red-700 cursor-pointer'>Delete Account</span>
|
<span className='text-red-700 cursor-pointer'>Delete Account</span>
|
||||||
<span className='text-red-700 cursor-pointer'>Log Out</span>
|
<span className='text-red-700 cursor-pointer'>Log Out</span>
|
||||||
</div>
|
</div>
|
||||||
|
<p className='text-red-700 mt-4'>{error ? error : ''}</p>
|
||||||
|
<p className='text-green-900 mt-4'>{updateSuccess ? 'User profile is updated successfully' : ''}</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,21 @@ const userSlice = createSlice({
|
||||||
loginFailure: (state, action) => {
|
loginFailure: (state, action) => {
|
||||||
state.error = action.payload;
|
state.error = action.payload;
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
}
|
},
|
||||||
|
updateUserStart: (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
},
|
||||||
|
updateUserSuccess: (state, action) => {
|
||||||
|
state.currentUser = action.payload;
|
||||||
|
state.loading = false;
|
||||||
|
state.error = null;
|
||||||
|
},
|
||||||
|
updateUserFailure: (state, action) => {
|
||||||
|
state.error = action.payload;
|
||||||
|
state.loading = false;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {loginStart, loginSuccess, loginFailure} = userSlice.actions;
|
export const {loginStart, loginSuccess, loginFailure, updateUserStart, updateUserSuccess, updateUserFailure} = userSlice.actions;
|
||||||
export default userSlice.reducer;
|
export default userSlice.reducer;
|
21
real_estate/package-lock.json
generated
21
real_estate/package-lock.json
generated
|
@ -10,6 +10,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
|
"cookie-parser": "^1.4.6",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.18.3",
|
"express": "^4.18.3",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
@ -227,6 +228,26 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cookie-parser": {
|
||||||
|
"version": "1.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
||||||
|
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": "0.4.1",
|
||||||
|
"cookie-signature": "1.0.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cookie-parser/node_modules/cookie": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cookie-signature": {
|
"node_modules/cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
|
"cookie-parser": "^1.4.6",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.18.3",
|
"express": "^4.18.3",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
|
|
@ -2,7 +2,6 @@ import User from "../models/user.model.js";
|
||||||
import bcryptjs from 'bcryptjs'
|
import bcryptjs from 'bcryptjs'
|
||||||
import { errorHandler } from "../utils/error.js";
|
import { errorHandler } from "../utils/error.js";
|
||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
import { userInfo } from "os";
|
|
||||||
|
|
||||||
export const signup = async(req, res, next) => {
|
export const signup = async(req, res, next) => {
|
||||||
const {username, email, password} = req.body
|
const {username, email, password} = req.body
|
||||||
|
@ -46,7 +45,7 @@ export const google = async(req, res, next) => {
|
||||||
const generatedPassword = Math.random().toString(36).slice(-8) + Math.random().toString(36).slice(-8);
|
const generatedPassword = Math.random().toString(36).slice(-8) + Math.random().toString(36).slice(-8);
|
||||||
const hashedPassword = bcryptjs.hashSync(generatedPassword, 10);
|
const hashedPassword = bcryptjs.hashSync(generatedPassword, 10);
|
||||||
const newUser = new User({
|
const newUser = new User({
|
||||||
username: req.body.name.split(" ").join("").toLowerCase()+Math.random().toString(36).slice(-4),
|
username: req.body.name.split(' ').join('').toLowerCase()+Math.random().toString(36).slice(-4),
|
||||||
email: req.body.email,
|
email: req.body.email,
|
||||||
password: hashedPassword,
|
password: hashedPassword,
|
||||||
avatar: req.body.photo,
|
avatar: req.body.photo,
|
||||||
|
@ -55,7 +54,6 @@ export const google = async(req, res, next) => {
|
||||||
const token = jwt.sign({id: newUser._id}, process.env.JWT_SECRET);
|
const token = jwt.sign({id: newUser._id}, process.env.JWT_SECRET);
|
||||||
const {password: pass, ...rest} = newUser._doc;
|
const {password: pass, ...rest} = newUser._doc;
|
||||||
res.cookie('access_token', token, {httpOnly: true}).status(200).json(rest);
|
res.cookie('access_token', token, {httpOnly: true}).status(200).json(rest);
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error)
|
next(error)
|
||||||
|
|
|
@ -1,5 +1,31 @@
|
||||||
|
import bcryptjs from 'bcryptjs';
|
||||||
|
import User from '../models/user.model.js';
|
||||||
|
import { errorHandler } from '../utils/error.js';
|
||||||
|
|
||||||
export const test = (req, res) => {
|
export const test = (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
message: 'API route is working',
|
message: 'API route is working',
|
||||||
})
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateUser = async (req, res, next) =>{
|
||||||
|
if(req.user.id !== req.params.id) return next(errorHandler(401, 'You can only update your account'))
|
||||||
|
try {
|
||||||
|
if(req.body.password){
|
||||||
|
req.body.password = bcryptjs.hashSync(req.body.password, 10)
|
||||||
|
}
|
||||||
|
const updatedUser = await User.findByIdAndUpdate(req.params.id, {
|
||||||
|
$set:{
|
||||||
|
username: req.body.username,
|
||||||
|
email: req.body.email,
|
||||||
|
password: req.body.password,
|
||||||
|
avatar: req.body.avatar,
|
||||||
|
}
|
||||||
|
}, {new: true})
|
||||||
|
const {password, ...rest} = updatedUser._doc
|
||||||
|
res.status(200).json(rest);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose';
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv';
|
||||||
import userRouter from './routes/user.route.js'
|
import userRouter from './routes/user.route.js';
|
||||||
import authRouter from './routes/auth.route.js'
|
import authRouter from './routes/auth.route.js';
|
||||||
|
import cookieParser from 'cookie-parser';
|
||||||
|
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
@ -18,6 +19,7 @@ mongoose.connect(process.env.MONGODB).then(() => {
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
app.use(cookieParser());
|
||||||
|
|
||||||
app.listen(3000, () =>{
|
app.listen(3000, () =>{
|
||||||
console.log('Server is running on port 3000')
|
console.log('Server is running on port 3000')
|
||||||
|
@ -31,7 +33,7 @@ app.use('/server/auth', authRouter)
|
||||||
app.use((err, req, res, next) => {
|
app.use((err, req, res, next) => {
|
||||||
const statusCode = err.statusCode || 500
|
const statusCode = err.statusCode || 500
|
||||||
const message = err.message || "Internal server error"
|
const message = err.message || "Internal server error"
|
||||||
return res.status(statusCode),json({
|
return res.status(statusCode).json({
|
||||||
success: false,
|
success: false,
|
||||||
statusCode,
|
statusCode,
|
||||||
message,
|
message,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { test } from '../controllers/user.controller.js';
|
import { test, updateUser } from '../controllers/user.controller.js';
|
||||||
|
import { verifyToken } from '../utils/verifyUser.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/test', test)
|
router.get('/test', test)
|
||||||
|
router.post('/update/:id', verifyToken, updateUser)
|
||||||
|
|
||||||
export default router
|
export default router
|
13
real_estate/server/utils/verifyUser.js
Normal file
13
real_estate/server/utils/verifyUser.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { errorHandler } from './error.js';
|
||||||
|
|
||||||
|
export const verifyToken = (req, res, next) => {
|
||||||
|
const token = req.cookies.access_token;
|
||||||
|
if(!token) return next(errorHandler(401, 'Unauthorized'));
|
||||||
|
|
||||||
|
jwt.verify(token, process.env.JWT_SECRET, (err, user) =>{
|
||||||
|
if(err) return next(errorHandler(403, 'Forbidden'));
|
||||||
|
req.user = user;
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue