-
아마존 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({})); }, [])
같은 방식으로 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); }))
'NODE.JS' 카테고리의 다른 글
아마존 E-commerce 클론 -26) Top Seller Carousel 추가하고, 장바구니 설정하기(동일 주문자의 상품만 장바구니에 추가가능) (0) 2021.05.29 아마존 E-commerce 클론 -25) Seller 정보페이지 만들기 (0) 2021.05.29 아마존 E-commerce 클론 -23) Admin user관리페이지 만들기 (0) 2021.05.29 아마존 E-commerce 클론 -22) Admin order관리페이지 만들기 (0) 2021.05.28 아마존 E-commerce 클론 -21) Admin product관리페이지 만들기 (0) 2021.05.28