-
아마존 E-commerce 클론 -23) Admin user관리페이지 만들기NODE.JS 2021. 5. 29. 11:32
모든 유저의 정보를 가지고 오는 userRouter를 만들어보자.
userRouter.get('/', isAuth,isAdmin, expressAsyncHandler(async(req, res)=>{ const users = await User.find({}) res.send(users); }))
UserListScreen.js
import React, { useEffect } from 'react' import { useDispatch } from 'react-redux' import LoadingBox from '../components/LoadingBox' import MessageBox from '../components/MessageBox' function UserListScreen() { const dispatch = useDispatch() useEffect(() => { dispatch(listUsers()) }, [dispatch]) return ( <div> <h1>Users</h1> { loading? <LoadingBox></LoadingBox>: error? <MessageBox variant='danger'>{error}</MessageBox> : ( <table className="table"> <thead> <tr> <th>ID</th> <th>NAME</th> <th>EMAIL</th> <th>IS SELLER</th> <th>IS ADMIN</th> <th>ACTIONS</th> </tr> </thead> <tbody> { users.map(user=>( <tr key = {user._id}> <td>{user._id}</td> <td>{user.name}</td> <td>{user.email}</td> <td>{user.isSeller? 'YES': 'NO'}</td> <td>{user.isAdmin? 'YES': 'NO'}</td> <td> <button className="small">Edit</button> <button className="small">Delete</button> </td> </tr> )) } </tbody> </table> ) } </div> ) } export default UserListScreen
userModel에 isseller 특징을 추가해주자.
isSeller:{ type:Boolean, default:false, required:true }
이제userConstants.js
export const USER_LIST_REQUEST = 'USER_LIST_REQUEST' export const USER_LIST_SUCCESS = 'USER_LIST_SUCCESS' export const USER_LIST_FAIL = 'USER_LIST_FAIL'
userActions.js
export const listUsers = () =>async(dispatch, getState)=>{ dispatch({ type: USER_LIST_REQUEST }) const {userSignin:{userInfo}} = getState(); try{ const {data} = await axios.get(`/api/users`, { headers:{Authorization: `Bearer ${userInfo.token}`} }) dispatch({ type: USER_LIST_SUCCESS, payload:data }) }catch(error){ const message = error.response && error.response.data.message ? error.response.data.message : error.message dispatch({ type: USER_LIST_FAIL, payload:message }) } }
userReducers.js
export const userListReducter = (state = {loading:true} ,action)=>{ switch(action.type){ case USER_LIST_REQUEST: return {loading:true} case USER_LIST_SUCCESS: return {loading:false, users:action.payload} case USER_LIST_FAIL: return {loading:false, error:action.payload} default: return state; } }
store.js
const reducer = combineReducers({ productList: productListReducer, productDetails : productDetailsReducer, cart:cartReducer, userSignin: userSigninReducer, userRegister: userRegisterReducer, orderCreate:orderCreateReducer, orderDetails:orderDetailsReducer, orderPay: orderPayReducer, orderMineList:orderMineListReducer, userDetails:userDetailsReducer, userUpdateProfile:userUpdateProfileReducer, productCreate:productCreateReducer, productUpdate:productUpdateReducer, productDelete:productDeleteReducer, orderList:orderListReducer, orderDelete:orderDeleteReducer, orderDeliver:orderDeliverReducer, userList:userListReducter })
UserListScreen.js
import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { listUsers } from '../actions/userActions' import LoadingBox from '../components/LoadingBox' import MessageBox from '../components/MessageBox' function UserListScreen() { const userList = useSelector(state => state.userList) const {loading, error, users} = userList const dispatch = useDispatch() useEffect(() => { dispatch(listUsers()) }, [dispatch]) return ( <div> <h1>Users</h1> { loading? <LoadingBox></LoadingBox>: error? <MessageBox variant='danger'>{error}</MessageBox> : ( <table className="table"> <thead> <tr> <th>ID</th> <th>NAME</th> <th>EMAIL</th> <th>IS SELLER</th> <th>IS ADMIN</th> <th>ACTIONS</th> </tr> </thead> <tbody> { users.map(user=>( <tr key = {user._id}> <td>{user._id}</td> <td>{user.name}</td> <td>{user.email}</td> <td>{user.isSeller? 'YES': 'NO'}</td> <td>{user.isAdmin? 'YES': 'NO'}</td> <td> <button className="small">Edit</button> <button className="small">Delete</button> </td> </tr> )) } </tbody> </table> ) } </div> ) } export default UserListScreen
app.js에 스크린 추가
import UserListScreen from './screens/UserListScreen'; <AdminRoute path="/userlist" component={UserListScreen} exact></AdminRoute>
이제 delete기능을 활성화 시키자.
api를 먼저 만들자.
userRouter.js
userRouter.delete('/:id', isAuth, isAdmin, expressAsyncHandler(async(req, res)=>{ const user = await User.findById(req.params.id) if(user){ const deletedUser = await user.remove(); res.send({message: 'User Deleted', user:deletedUser}) }else{ res.status(404).send({message:'User Not Found'}) } }))
userConstants.js
export const USER_DELETE_REQUEST = 'USER_DELETE_REQUEST' export const USER_DELETE_SUCCESS = 'USER_DELETE_SUCCESS' export const USER_DELETE_FAIL = 'USER_DELETE_FAIL' export const USER_DELETE_RESET = 'USER_DELETE_RESET'
userActions.js
export const deleteUser = (userId) =>async(dispatch, getState)=>{ dispatch({ type: USER_DELETE_REQUEST, payload:userId }) const {userSignin:{userInfo}} = getState(); try{ const {data} = await axios.delete(`/api/users/${userId}`, { headers:{Authorization: `Bearer ${userInfo.token}`} }) dispatch({ type: USER_DELETE_SUCCESS, payload:data }) }catch(error){ const message = error.response && error.response.data.message ? error.response.data.message : error.message dispatch({ type: USER_DELETE_FAIL, payload:message }) } }
userReducers.js
새로운페이지로의 이동이 아니기 때문에 (버튼클릭) state=loading 넣어줄 필요 없다.
export const userDeleteReducer = (state={}, action)=>{ switch(action.type){ case USER_DELETE_REQUEST: return {loading:true} case USER_DELETE_SUCCESS: return {loading:false, success:true} case USER_DELETE_FAIL: return {loading:false, error:action.payload} case USER_DELETE_RESET: return {}; default: return state; } }
store.js
const reducer = combineReducers({ productList: productListReducer, productDetails : productDetailsReducer, cart:cartReducer, userSignin: userSigninReducer, userRegister: userRegisterReducer, orderCreate:orderCreateReducer, orderDetails:orderDetailsReducer, orderPay: orderPayReducer, orderMineList:orderMineListReducer, userDetails:userDetailsReducer, userUpdateProfile:userUpdateProfileReducer, productCreate:productCreateReducer, productUpdate:productUpdateReducer, productDelete:productDeleteReducer, orderList:orderListReducer, orderDelete:orderDeleteReducer, orderDeliver:orderDeliverReducer, userList:userListReducter, userDelete:userDeleteReducer })
userListScreen.js
import { deleteUser, listUsers } from '../actions/userActions' const userDelete = useSelector(state => state.userDelete) const {loading:loadingDelete, error:errorDelete, success:successDelete} = userDelete useEffect(() => { dispatch(listUsers()) }, [dispatch, successDelete])//삭제가되면 리스트 다시 가져오도록 설정 const deleteHandler = (user)=>{ if(window.confirm('Are you sure to delete?')){ dispatch(deleteUser(user._id)) } } return ( <div> <h1>Users</h1> {loadingDelete && <LoadingBox></LoadingBox>} {errorDelete && <MessageBox variant='danger'>{errorDelete}</MessageBox>} {successDelete && <MessageBox variant='success'>User Deleted Successfully</MessageBox>} { <td> <button type = "button" className="small" >Edit</button> <button type = "button" className="small" onClick ={()=>deleteHandler(user)} >Delete</button> </td>
여기서 추가로 admin계정은 삭제하지 못하도록 설정하자.
userRouter.js
userRouter.delete('/:id', isAuth, isAdmin, expressAsyncHandler(async(req, res)=>{ const user = await User.findById(req.params.id) if(user){ if(user.email === 'admin@example.com'){ res.status(400).send({message:'Can Not Delete Admin User'}) return; } const deletedUser = await user.remove(); res.send({message: 'User Deleted', user:deletedUser}) }else{ res.status(404).send({message:'User Not Found'}) } })) export default userRouter;
난 isAdim ===true일때 삭제 못하는 것으로 설정하겠다.
userRouter.delete('/:id', isAuth, isAdmin, expressAsyncHandler(async(req, res)=>{ const user = await User.findById(req.params.id) if(user){ if(user.isAdmin === true){ res.status(400).send({message:'Can Not Delete Admin User'}) return; } const deletedUser = await user.remove(); res.send({message: 'User Deleted', user:deletedUser}) }else{ res.status(404).send({message:'User Not Found'}) } })) export default userRouter;
이제 edit user 기능을 만들자.
우선UserEditScreen.js를 만들자(형식잡기)
import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { detailsUser } from '../actions/userActions' import LoadingBox from '../components/LoadingBox' import MessageBox from '../components/MessageBox' function UserEditScreen(props) { const userId = props.match.params.id const [name, setName] = useState('') const [email, setEmail] = useState('') const [isSeller, setIsSeller] = useState(false) const [isAdmin, setIsAdmin] = useState(false) const userDetails = useSelector(state => state.userDetails) const {loading, error,user } = userDetails const dispatch = useDispatch() useEffect(() => { if(!user){ dispatch(detailsUser(userId)); }else{ setName(user.name); setEmail(user.email) setIsSeller(user.isSeller) setIsAdmin(user.isAdmin) } const submitHandler = (e)=>{ e.preventDefault(); } return ( <div> <form className="form" onSubmit={submitHandler}> <h1>Edit User</h1> <div> {loading? <LoadingBox></LoadingBox> : error? <MessageBox variant="danger">{error}</MessageBox> : ( <> <div> <label htmlFor="name">Name</label> <input type="text" id="name" placeholder="Enter name" value={name} onChange={(e)=> setName(e.target.value)} /> </div> <div> <label htmlFor="email">Email</label> <input type="email" id="email" placeholder="Enter email" value={email} onChange={(e)=> setEmail(e.target.value)} /> </div> <div> <label htmlFor="isSeller">Is Seller</label> <input type="checkbox" id="isSeller" checked={isSeller} onChange={(e)=> setIsSeller(e.target.checked)} /> </div> <div> <label htmlFor="isAdmin">Is Admin</label> <input type="checkbox" id="isAdmin" checked={isAdmin} onChange={(e)=> setIsAdmin(e.target.checked)} /> </div> <div> <button className="primary" type="submit">Update</button> </div> </> ) } </div> </form> </div> ) } export default UserEditScreen
app.js에 스크린 추가
import UserEditScreen from './screens/UserEditScreen'; <AdminRoute path="/user/:id/edit" component={UserEditScreen} exact></AdminRoute>
userListScreen에서 edit 버튼 누르면 페이지를 이동하도록 해주자.
function UserListScreen(props) { <button type = "button" className="small" onClick={()=>props.history.push(`/user/${user._id}/edit`)} >Edit</button>
유저정보를 업데이트하는 api를 만들자.
userRouter.put( '/:id', isAuth, isAdmin, expressAsyncHandler(async (req, res) => { const user = await User.findById(req.params.id); if (user) { user.name = req.body.name || user.name; user.email = req.body.email || user.email; user.isSeller =req.body.isSeller === user.isSeller ? user.isSeller : req.body.isSeller; //Boolean(req.body.isSeller) user.isAdmin =req.body.isAdmin === user.isAdmin ? user.isAdmin : req.body.isAdmin;//Boolean(req.body.isAdmin) const updatedUser = await user.save(); res.send({ message: 'User Updated', user: updatedUser }); } else { res.status(404).send({ message: 'User Not Found' }); } }) ); export default userRouter;
userConstants.js
export const USER_UPDATE_REQUEST = 'USER_UPDATE_REQUEST' export const USER_UPDATE_SUCCESS = 'USER_UPDATE_SUCCESS' export const USER_UPDATE_FAIL = 'USER_UPDATE_FAIL' export const USER_UPDATE_RESET = 'USER_UPDATE_RESET'
userActions.js
export const updateUser = (user)=> async (dispatch, getState)=>{ dispatch({ type: USER_UPDATE_REQUEST, payload:user }) const {userSignin:{userInfo}} = getState(); try{ const {data} = await axios.put(`/api/users/${user._id}`, user, { headers:{Authorization: `Bearer ${userInfo.token}`} }) dispatch({ type: USER_UPDATE_SUCCESS, payload:data }) }catch(error){ const message = error.response && error.response.data.message ? error.response.data.message : error.message dispatch({ type: USER_UPDATE_FAIL, payload:message }) } }
userReducers.js
export const userUpdateReducer = (state={}, action)=>{ switch(action.type){ case USER_UPDATE_REQUEST: return {loading:true} case USER_UPDATE_SUCCESS: return {loading:false, success:true} case USER_UPDATE_FAIL: return {loading:false, error:action.payload} case USER_UPDATE_RESET: return {}; default: return state; } }
store.js
const reducer = combineReducers({ productList: productListReducer, productDetails : productDetailsReducer, cart:cartReducer, userSignin: userSigninReducer, userRegister: userRegisterReducer, orderCreate:orderCreateReducer, orderDetails:orderDetailsReducer, orderPay: orderPayReducer, orderMineList:orderMineListReducer, userDetails:userDetailsReducer, userUpdateProfile:userUpdateProfileReducer, productCreate:productCreateReducer, productUpdate:productUpdateReducer, productDelete:productDeleteReducer, orderList:orderListReducer, orderDelete:orderDeleteReducer, orderDeliver:orderDeliverReducer, userList:userListReducter, userDelete:userDeleteReducer, userUpdate:userUpdateReducer })
userEditScreen.js
import { useDispatch, useSelector } from 'react-redux' import { detailsUser, updateUser } from '../actions/userActions' import { USER_UPDATE_RESET } from '../constants/userConstants' function UserEditScreen(props) { const userUpdate = useSelector(state => state.userUpdate) const {loading:loadingUpdate, error:errorUpdate, success:successUpdate} = userUpdate const dispatch = useDispatch() useEffect(() => { if(successUpdate){ dispatch({ type:USER_UPDATE_RESET }) props.history.push('/userList') } if(!user){ dispatch(detailsUser(userId)); }else{ setName(user.name); setEmail(user.email) setIsSeller(user.isSeller) setIsAdmin(user.isAdmin) } }, [dispatch,props.history, user, userId, successUpdate]) const submitHandler = (e)=>{ e.preventDefault(); dispatch(updateUser({_id:userId, name, email, isSeller, isAdmin})) } return ( <div> <form className="form" onSubmit={submitHandler}> <h1>Edit User</h1> <div> {loadingUpdate && <LoadingBox></LoadingBox>} {errorUpdate && <MessageBox variant='danger'>{errorUpdate}</MessageBox>} {loading? <LoadingBox></LoadingBox> : error? <MessageBox variant="danger">{error}</MessageBox> :
추가로 유저 리스트를 가지고 오고 나면 디테일을 리셋해주는 기능을 할건데,
user_DETAILS_RESET을 추가로 만들어준다.
export const USER_DETAILS_RESET = 'USER_DETAILS_RESET'
추가
export const userDetailsReducer = (state = {loading:true} ,action)=>{ switch(action.type){ case USER_DETAILS_REQUEST: return {loading:true} case USER_DETAILS_SUCCESS: return {loading:false, user:action.payload} case USER_DETAILS_FAIL: return {loading:false, error:action.payload} case USER_DETAILS_RESET: return {loading:true}; default: return state; } }
userListScreen을 다음과 같이 수정해준다.
useEffect(() => { dispatch(listUsers()) dispatch({ type:USER_DETAILS_RESET }) }, [dispatch, successDelete])
'NODE.JS' 카테고리의 다른 글
아마존 E-commerce 클론 -25) Seller 정보페이지 만들기 (0) 2021.05.29 아마존 E-commerce 클론 -24) Seller 페이지 만들기 (Product, Order) (0) 2021.05.29 아마존 E-commerce 클론 -22) Admin order관리페이지 만들기 (0) 2021.05.28 아마존 E-commerce 클론 -21) Admin product관리페이지 만들기 (0) 2021.05.28 아마존 E-commerce 클론 -20) UserProfile screen 만들고, Admin 미들웨어 만들기 (0) 2021.05.28