ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 E-commerce 클론 -24) Seller 페이지 만들기 (Product, Order)
    NODE.JS 2021. 5. 29. 18:00

     

     

     

    셀러들이 관리하는 페이지를 만들어보자. 

    셀러로 등록된 사람들은 상품과 들어온 주문들을 관리할 수 있다. 

     

    먼저 app.js에서 셀러 메뉴를 만들어보자. 

                  {
                    userInfo && userInfo.isSeller && (
                      <div className="dropdown">
                        <Link to="#seller">Seller {' '}<i className="fa fa-caret-down"></i> </Link>
                        <ul className="dropdown-content">
                          <li>
                            <Link to = "/productlist/seller">Products</Link>
                          </li>
                          <li>
                            <Link to = "/orderlist/seller">Orders</Link>
                          </li>
                        </ul>
                      </div>
                    )
                  }
                  {userInfo && userInfo.isAdmin && (
                  
                  
                  
                  
                  
                  

     

     

    AdminRoute처럼 셀러들만 들어갈 수 있는 SellerRoute를 만들어주자. 

    components>SellerRoute.js

    import React from 'react'
    import { useSelector } from 'react-redux'
    import { Redirect, Route } from 'react-router-dom'
    
    function SellerRoute({component:Component, ...rest}) {
        const userSignin = useSelector(state=>state.userSignin)
        const {userInfo} = userSignin
        return (
            <Route {...rest}  render = {(props)=>userInfo && userInfo.isSeller? (<Component {...props}></Component>):
        (
            <Redirect to="/signin"/>
        )}
        ></Route>
        )
    }
        
    
    export default SellerRoute
    

     

    app.js에 라우트를추가해준다. 

    여기서 상품리스트와 주문리스트페이지를 편집해서 나타내도록 할것이다. 

    import SellerRoute from './components/SellerRoute';
                  <SellerRoute path="/productlist/seller" component={ProductListScreen} exact></SellerRoute>          
                  <SellerRoute path="/orderlist/seller" component={OrderListScreen} exact></SellerRoute>   

     

    useModel.js에 seller항목을 추가해준다. 

        seller:{
            name:String,
            logo:String,
            description:String,
            rating:{type:Number, default:0, required:true},
            numReviews:{type:Number, default:0, required:true}
        }

     

    profileScreen.js에 가서 셀러항목을 추가해준다. 

        const [sellerName, setSellerName] = useState('')
        const [sellerLogo, setSellerLogo] = useState('')
        const [sellerDescription, setSellerDescription] = useState('')
    
    
        const dispatch = useDispatch()
        useEffect(()=>{
            if(!user){
                dispatch({
                    type:USER_UPDATE_PROFILE_RESET
                })
                dispatch(detailsUser(userInfo._id));
            }else{
                setname(user.name)
                setemail(user.email)
                if(user.isSeller){
                    setSellerName(user.seller.name)
                    setSellerLogo(user.seller.logo)
                    setSellerDescription(user.seller.description)
                }
            }
        },[dispatch, userInfo._id, user, sellerName, sellerLogo, sellerDescription])
        const submitHandler = (e) =>{
            e.preventDefault();
            if(password!==confirmPassword){
                alert('Password and Confirm Password Are Not Matched')
            }else{
                dispatch(updateUserProfile({userId:user._id, name, email, password,sellerName, sellerLogo, sellerDescription}))
            }
        }
        
        
        
        
        
                         <input id = "confirmPassword" placeholder="Enter confirmPassword" type="password" onChange={e=>setconfirmPassword(e.target.value)} />
                            </div>
                            {
                                user.isSeller && (
                                    <>
                                    <h2>Seller</h2>
                                    <div>
                                        <label htmlFor="sellerName">Seller Name</label>
                                        <input type="text" id="sellerName" placeholder="Enter seller name" value = {sellerName} onChange={(e)=>setSellerName(e.target.value)}/>
                                    </div>
                                    <div>
                                        <label htmlFor="sellerLogo">Seller Logo</label>
                                        <input type="text" id="sellerLogo" placeholder="Enter seller logo" value = {sellerLogo} onChange={(e)=>setSellerLogo(e.target.value)}/>
                                    </div>
                                    <div>
                                        <label htmlFor="sellerDescription">Seller Description</label>
                                        <input type="text" id="sellerDescription" placeholder="Enter seller Description" value = {sellerDescription} onChange={(e)=>setSellerDescription(e.target.value)}/>
                                    </div>
                                    </>
                                )
                            }
                            <div>

     

    userRouter.js 에서 updateProfile할때 사용하는 라우트를 다음과 같이 수정해주자. 

    userRouter.put('/profile', isAuth, expressAsyncHandler(async(req, res)=>{
      const user = await User.findById(req.user._id);
      if(user){
        user.name = req.body.name || user.nmae;
        user.email = req.body.email||user.email;
        if(user.isSeller){
          user.seller.name = req.body.sellerName|| user.seller.name;
          user.seller.logo = req.body.sellerLogo|| user.seller.logo;
          user.seller.description = req.body.sellerDescription|| user.seller.description;
    
        }
        if(req.body.password){
          user.password = bcrypt.hashSync(req.body.password, 8);
        }
        const updateUser = await user.save();
        res.send({
          _id:updateUser._id ,
          email: updateUser.email,
          name: updateUser.name,
          isAdmin: updateUser.isAdmin,
          token: generateToken(updateUser),
        })
      }
    }))

     

     

    이제 로그인할때 user의 Seller 정보도 같이 가져올 수 있도록 유저 로그인 라우트를 수정해보자. 

    또 로그인할때도, 프로필을 업데이트할때도 적용되도록 수정하자. 

    userRouter.post('/signin', expressAsyncHandler(async (req, res)=>{
      const user = await User.findOne({email:req.body.email});
      if(user){
        if(bcrypt.compareSync(req.body.password, user.password)){
          res.send({
            _id:user.id,
            name:user.name,
            email:user.email,
            isAdmin:user.isAdmin,
            isSeller:user.isSeller,//이부분 추가
            token:generateToken(user)
          });
          return ;
        }
      }
      res.status(401).send({message:"Invalid email or password"})
    }))
    
    
      
    userRouter.post('/register', expressAsyncHandler(async(req, res)=>{
      const user = new User({
                    name: req.body.name, 
                    email:req.body.email, 
                    password: bcrypt.hashSync(req.body.password, 8)
                    })
      const createdUser = await user.save();
      res.send({
        _id:createdUser.id,
        name:createdUser.name,
        email:createdUser.email,
        isAdmin:user.isAdmin,
        isSeller:user.isSeller,
        token:generateToken(createdUser)
      });
    }))
    
    
    
    userRouter.put('/profile', isAuth, expressAsyncHandler(async(req, res)=>{
      const user = await User.findById(req.user._id);
      if(user){
        user.name = req.body.name || user.nmae;
        user.email = req.body.email||user.email;
        if(user.isSeller){
          user.seller.name = req.body.sellerName|| user.seller.name;
          user.seller.logo = req.body.sellerLogo|| user.seller.logo;
          user.seller.description = req.body.sellerDescription|| user.seller.description;
    
        }
        if(req.body.password){
          user.password = bcrypt.hashSync(req.body.password, 8);
        }
        const updateUser = await user.save();
        res.send({
          _id:updateUser._id ,
          email: updateUser.email,
          name: updateUser.name,
          isAdmin: updateUser.isAdmin,
          isSeller:uadateUser.isSeller,
          token: generateToken(updateUser),
        })
      }
    }))

     

     

    utils.js에서도 토큰을 생성하고나서 isSeller 정보를 전달하도록 수정한다. 

    import jwt from 'jsonwebtoken';
     
    
    export const generateToken = (user)=>{
        return jwt.sign({
            _id:user._id, 
            name:user.name,
            email:user.email,
            isAdmin: user.isAdmin,
            isSeller:user.isSeller
        }, process.env.JWT_SECRET || 'somethingsecret', {
            expiresIn: '30d'
        })
    }
    

    다시 로그인하면 다음과 같이 확인 가능
    프로필 편집에서도 확인이 가능하다. 

    여기서 업데이트 눌르고 다시 들어왔을때, 

    내가입력했던 내용들을 볼 수 있도록 수정해보자. 

    ProfileEditScreen.js

        const dispatch = useDispatch()
        useEffect(()=>{
            if(!user){
                dispatch({
                    type:USER_UPDATE_PROFILE_RESET
                })
                dispatch(detailsUser(userInfo._id));
            }else{
                setname(user.name)
                setemail(user.email)
                if(user.seller){
                    setSellerName(user.seller.name)
                    setSellerLogo(user.seller.logo)
                    setSellerDescription(user.seller.description)
                }
            }
        },[dispatch, userInfo._id, user])//여기서 셀러 정보들 삭제했다. (이름, 이메일, 셀러정보 바꿨다고 로딩다시시키지 않도록)
    

    새로고침해도 입력했던 내용 그대로 있다. 

     

     

     

    이제 셀러의 상품만 보이도록 하자.

    productModel.js로 먼저간다. 다음과 같이 셀러항목을 추가해준다.

        seller:{
            type:mongoose.Schema.Types.ObjectId
            ref:'User',
    
        },

    productRouter로 가서 새 상품을 등록할때 seller항목도 등록하도록 설정하자. 

    admin유저만 등록할 수 있고, 셀러항목도 추가로 넣었다. 

    productRouter.post('/', isAuth, isAdmin, expressAsyncHandler(async(req, res)=>{
        const product =  new Product({
            name: 'sample name'+Date.now(),
            seller: req.user._id,
            image:'/images/product-1.jpeg',
            price:0,
            category:'sample caategory',
            brand:'sample brand',
            countInStock: 0,
            rating: 2,
            numReviews:0,
            description:'sample description'
    
        })
        const createdProduct = await product.save();
        res.send({message:'Product Created', product: createdProduct})
    }))

    그런데 admin유저만 상품을 등록할 수 있는건 우리가 원하는게아니다. 

    isSeller도 상품을 등록하도록 하자 

    utils.js로 가서 isSeller를 만들어주자. 

    또 추가로 admin 또는 seller 모두 행할 수 있는 isAdminOrSeller도 만들어주자. 

    export const isSeller = (req, res, next)=>{
        if(req.user && req.user.isSeller){
            next();
        }else{
            res.status(401).send({message:'Invalid Seller Token'})
        }
    }
    export const isSellerOrAdmin = (req, res, next)=>{
        if(req.user && (req.user.isSeller || req.user.isAdmin)){
            next();
        }else{
            res.status(401).send({message:'Invalid Seller or Admin Token'})
        }
    }

     

    다시 프로덕트라우터로가서 seller와 admin모두 상품이 등록가능하게 다음과 같이 수정한다. 

    추가로 seller, admin모두 상품 수정도 가능하게바꿔준다. 

     

    productRouter.post('/', isAuth, isSellerOrAdmin, expressAsyncHandler(async(req, res)=>{
        const product =  new Product({
            name: 'sample name'+Date.now(),
            seller: req.user._id,
            image:'/images/product-1.jpeg',
            price:0,
            category:'sample caategory',
            brand:'sample brand',
            countInStock: 0,
            rating: 2,
            numReviews:0,
            description:'sample description'
    
        })
        const createdProduct = await product.save();
        res.send({message:'Product Created', product: createdProduct})
    }))
    
    
    
    productRouter.put('/:id', isAuth, isSellerOrAdmin, expressAsyncHandler(async(req, res)=>{
        const productId = req.params.id;
        const product = await Product.findById(productId)
        if(product){
            product.name = req.body.name;
            product.price = req.body.price;
            product.image = req.body.image;
            product.category = req.body.category;
            product.brand = req.body.brand;
            product.countInStock = req.body.countInStock;
            product.description = req.body.description;
            const updatedProduct = await product.save();
            res.send({meassge:'Product Updated', product:updatedProduct})
        }else{
            res.status(404).send({message:'Product Not Found'})
        }
    }))

     

    이제 쿼리에 셀러가 있다면 셀러상품만 보이도록 .get('/'~)을 수정해보자. 

    productRouter.get('/' ,expressAsyncHandler(async(req, res)=>{
        const seller = req.query.seller || ''
        const sellerFilter = seller? {seller} :{};
        const products = await Product.find({...sellerFilter});
        res.send(products);
    }));
    

    productActions.js

     

    export const listProducts = ({seller=''}) =>async(dispatch)=>{
        dispatch({
            type:PRODUCT_LIST_REQUEST
        });
        try{
            const {data} = await Axios.get(`/api/products?seller=${seller}`);
            dispatch({type:PRODUCT_LIST_SUCCESS, payload:data});
        }catch(error){
            dispatch({type:PRODUCT_LIST_FAIL, payload:error.message});
        }
    }
    

     

     

    product ListScreen.js를 수정해보자. 

    function ProductListScreen(props) {
        const sellerMode = props.match.path.indexOf('/seller') >=0;
    
    
        const userSignin = useSelector(state => state.userSignin)
        const {userInfo} = userSignin
        
        
        const userSignin = useSelector(state => state.userSignin)
        const {userInfo} = userSignin
        useEffect(() => {
            if(successCreate){
                dispatch({
                    type:PRODUCT_CREATE_RESET
                })
                props.history.push(`/product/${createdProduct._id}/edit`)
            }
            if(successDelete){
                dispatch({type:PRODUCT_DELETE_RESET})
            }
            dispatch(listProducts({seller:sellerMode? userInfo._id :''}))//셀러모드가 true면 아이디 보낸다.
        }, [createdProduct, dispatch, props.history, successCreate, successDelete,sellerMode])
    

     

    홈스크린으로가서 listProducts를 디스패치할때 {}빈 배열을 넣어줘서 seller를 저장하지 않아 모든 상품이 뜨도록 한다. 

        useEffect(() => {
            dispatch(listProducts({}));
        }, [])

     

    관리자가 products로 가면 모든 상품리스트를 볼 수 있고
    셀러모드로 들어가면 자신이 등록한 상품만 볼 수 있다. 
    새로운 상품하나 만들고서 다시셀러모드로 들어가면 자신이 만든 상품 볼 수 있다. 

     

     

    같은 방식으로 orderview도 만들어보자. 

    먼저 order모델이 seller를 추가한다. (user정보 다음에 추가해주자)

        seller:{
            type:mongoose.Schema.Types.ObjectId,
            ref:'User',
    
        },

     

    orderrouter로 가서 수정해준다. 

    orderRouter.get('/', isAuth, isAdmin, expressAsyncHandler(async(req, res)=>{
        const seller = req.query.seller || ''
        const sellerFilter = seller? {seller} :{};
        const orders = await Order.find({...sellerFilter}).populate('user', 'name');
        res.send(orders);
    }))
    
    
    orderRouter.post('/', isAuth, expressAsyncHandler(async(req, res) => {
        if(req.body.orderItems.length===0){
            res.status(400).send({message:'Cart is empty'})
        }else{
            const order = new Order({
                seller:req.body.orderItems[0].seller,//첫번째 상품의 셀러정보를 등록
                orderItems: req.body.orderItems,
                shippingAddress: req.body.shippingAddress,
                paymentMethod: req.body.paymentMethod,
                itemsPrice: req.body.itemsPrice,
                shippingPrice: req.body.shippingPrice,
                taxPrice: req.body.taxPrice,
                totalPrice: req.body.totalPrice,
                user:req.user._id
            })
            const createdOrder = await order.save();
            res.status(201).send({message:'New Order reated', order:createdOrder})
        }
    }))

     

     

    cartActions로 가자. 

    카트에 추가할때 seller정보도 등록해준다. 

    export const addToCart = (productId, qty)=> async(dispatch, getState)=>{
        const {data} =await Axios.get(`/api/products/${productId}`);
        dispatch({
            type:CART_ADD_ITEM,
            payload:{
                name:data.name,
                image:data.image,
                price:data.price,
                countInStock:data.countInStock,
                product:data._id,
                seller:data.seller,//셀러정보 등록
                qty,
            }
        });
        localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems));
    }

    셀러 정보가 잘 들어왔다. 
    주문하면 주문한것중 첫번째 상품의 셀러정보가 잘 들어간다. 

     

     

    이제 주문도 seller와 연관된 주문만 찾도록 설정해보자. 

     

    orderlistScreen.을다음과 같이 추가하고 수정한다.

    function OrderListScreen(props) {
        const sellerMode = props.match.path.indexOf('/seller') >=0;
        const userSignin = useSelector(state => state.userSignin)
        const {userInfo} = userSignin
        
        const dispatch = useDispatch();
        useEffect(() => {
            dispatch({
                type:ORDER_DELETE_RESET
            })
    
            dispatch(listOrders({seller: sellerMode? userInfo._id: ''}));
        }, [dispatch,successDelete])
    

    orderActions.js로 가서 다음 과 같이 오더리스트를 가져오는 것을 바꿔준다.

     

    export const listOrders = ({seller = ''})=> async(dispatch, getState) =>{
        dispatch({
            type:ORDER_LIST_REQUEST
        })  
        const {userSignin:{userInfo}} = getState();
        try{
            const {data} = await axios.get(`/api/orders?seller=${seller}`, {
                headers:{Authorization:`Bearer ${userInfo.token}`}
            })
            dispatch({
                type:ORDER_LIST_SUCCESS,
                payload:data
            })
        }catch(error){
            const message = error.response && error.response.data.message
            ? error.response.data.message
            : error.message;
            dispatch({
                type:ORDER_LIST_FAIL,
                payload: message
            })
        }

     

     

     

    orderLIst를 가져오는 것을 orderRouter에서 셀러나 관리자 모두 가져올 수 있도록 설정한다. 

    orderRouter.get('/', isAuth, isSellerOrAdmin, expressAsyncHandler(async(req, res)=>{
        const seller = req.query.seller || ''
        const sellerFilter = seller? {seller} :{};
        const orders = await Order.find({...sellerFilter}).populate('user', 'name');
        res.send(orders);
    }))
    

    셀러에 맞는 주문만 확인할 수 있다. 

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.