ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 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])
    

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.