ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 E-commerce 클론 -27) Search Box ,Search Filter기능 , Side bar, sort & Filter 만들기
    NODE.JS 2021. 5. 29. 22:42

     

    우선 serchbox component를 만들자. 

    components>SearchBox.js

    import React, { useState } from 'react'
    
    function SearchBox(props) {
        const [name, setName] = useState('')
        const submitHandler=(e)=>{
            e.preventDefault();
            props.history.push(`/search/name/${name}`)
        }
        return (
            <form onSubmit={submitHandler} className="search">
                <div className="row">
                    <input type="text" name="q" id = "q" onChange={e=>setName(e.target.value)} />
                    <button className="primary" type="submit"><i className="fa fa-search"></i></button>
                </div>
            </form>
        )
    }
    
    export default SearchBox
    

     

    app.js에 추가하는데 route의 render를 이용해서 history기능도 사용할 수 있도록 한다. 

                <div>
                  <Link className="brand" to="/">amazona</Link>
                </div>
                <div>
                  <Route render = {({history})=>( <SearchBox history={history}></SearchBox>)}></Route>
                </div>

     

    style추가 

    /* Search */
    .search button{
      border-radius:0  0.5rem 0.5rem 0;
      border-right: none;
      margin-right: 0.5rem;
    }
    .search input{ 
      border-radius: 0.5rem 0 0 0.5rem;
      border-right: none;
      margin-left: 0.5rem;
    }

     

     

    검색하면 주소가 잘 이동한다. 

     

     

     

    이제 SearchScreen.js를 만들어준다. 

    상품리스트를 가져와서 보여주는 전체적인 템플릿을 만든다. 

    import React, { useEffect } from 'react'
    import { useDispatch, useSelector } from 'react-redux'
    import { useParams } from 'react-router'
    import { listProducts } from '../actions/productActions'
    import LoadingBox from '../components/LoadingBox';
    import MessageBox from '../components/MessageBox';
    import Product from '../components/Product';
    
    
    function SearchScreen(props) {
        const {name = 'all'} = useParams();
        const productList = useSelector(state => state.productList)
        const {loading, error, products} = productList
        const dispatch = useDispatch()
        useEffect(() => {
            dispatch(listProducts({name:name!=='all'? name: ''}))
        }, [dispatch,name])
        return (
            <div>
                <div className="row top">
                    {loading? <LoadingBox></LoadingBox> :
                    error? <MessageBox variant='danger'>{error}</MessageBox> :
                    <div>
                        {products.length} Results
                    </div>
                    }
                </div>
                <div className="row">
                    <div className="co-1">
                        <h3>Department</h3>
                        <ul>
                            <li>Caterory 1</li>
                        </ul>
                    </div>
                    <div className="col-3">
                        {loading? <LoadingBox></LoadingBox> :
                        error? <MessageBox variant='danger'>{error}</MessageBox> :
                        <div>
                            {products.length===0 &&  <MessageBox>No Product Found</MessageBox>}
                            <div className="row center">
                                {products.map(product=>(
                                    <Product key={product._id}  product = {product}/> 
                                ))}
                            </div>
                        </div>
                        }
                    </div>
                </div>
            </div>
        )
    }
    
    export default SearchScreen
    

     

     

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

    import SearchScreen from './screens/SearchScreen';
    
                  <Route path="/search/name/:name?" component={SearchScreen} exact></Route>
    

     

     

     

    이제 상품이 일치하는 상품만 나오도록 설정하자. 

     

    productActions.js

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

     

     

    productRouter.js에 다음과 같은 검사항목을 추가해준다. 

    productRouter.get('/' ,expressAsyncHandler(async(req, res)=>{
        const seller = req.query.seller || ''
        const name = req.query.name || ''
        const sellerFilter = seller? {seller} :{};
        const nameFilter = name? {name:{$regex:name, $options:'i'}} :{};
        const products = await Product.find({...sellerFilter, ...nameFilter}).populate('seller', 'seller.name seller.logo');
        res.send(products);
    }));
    

    맞는 검사항목만 보여준다. 

     

    이제 카테고리별로 나타내도록 필터를 만들자. 

     

     

    먼저 productrouter에 api를 만든다. 

    (list 아래에 만들기)

    productRouter.get('/categories' ,expressAsyncHandler(async(req, res)=>{
        const categories = await Product.find().distinct('category')
        res.send(categories)
    }));

     

    productConstants로 먼저 가보자. 

    export const PRODUCT_CATEGORY_LIST_REQUEST = 'PRODUCT_CATEGORY_LIST_REQUEST';
    export const PRODUCT_CATEGORY_LIST_SUCCESS = 'PRODUCT_CATEGORY_LIST_SUCCESS';
    export const PRODUCT_CATEGORY_LIST_FAIL = 'PRODUCT_CATEGORY_LIST_FAIL';
    

     

    productActions

     

    productlist와 비슷하므로 복사해서 수정해준다. 

    export const listProductsCategories = () =>async(dispatch)=>{
        dispatch({
            type:PRODUCT_CATEGORY_LIST_REQUEST
        });
        try{
            const {data} = await Axios.get(`/api/products/categories`);
            dispatch({type:PRODUCT_CATEGORY_LIST_SUCCESS, payload:data});
        }catch(error){
            dispatch({type:PRODUCT_CATEGORY_LIST_FAIL, payload:error.message});
        }
    }

     

     

     

    productReducers

    export const productCategoryListReducer = (state = { loading:true, categories: []}, action)=>{
        switch(action.type){
            case PRODUCT_CATEGORY_LIST_REQUEST:
                return {loading: true};
            case PRODUCT_CATEGORY_LIST_SUCCESS:
                return {loading:false, categories:action.payload};
            case PRODUCT_CATEGORY_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:userListReducer,
        userDelete:userDeleteReducer,
        userUpdate:userUpdateReducer,
        userTopSellersList:userTopSellerListReducer,
        productCategoryList:productCategoryListReducer
    })

     

     

     

     

    App.js안에 필터항목을 나타내보자.(sidebar로 나타낼 것이다) 

    import { listProductsCategories } from './actions/productActions';
    
    useEffect(() => {
        dispatch(listProductsCategories())
      }, [])
    

     

    searchScreen에도 보이도록 하자. 

    const {name = 'all', category='all'} = useParams();
    
    const productCategoryList = useSelector(state => state.productCategoryList)
    const {loading:loadingCategories, error:errorCategories, categories} = productCategoryList
    
    useEffect(() => {
            dispatch(listProducts({name:name!=='all'? name: '', category:category !=='all'? category:''}))
        }, [category, dispatch,name])
    
    
    useEffect(()=>{
            dispatch(listProductsCategories())
        },[dispatch])
    
    
    const getFilterUrl = (filter) =>{
        const filterCategory = filter.category ||category;
        const filterName = filter.name ||name;
        return `/search/category/${filterCategory}/name/${filterName}`
        }
        
        
        		<div className="row top">
                    <div className="col-1">
                        <h3>Department</h3>
                        {loadingCategories? <LoadingBox></LoadingBox> :
                        errorCategories? <MessageBox variant='danger'>{errorCategories}</MessageBox> :
                        (
                            <ul>
                                {categories.map(c=>
                                    (
                                        <li key = {c}>
                                            <Link to ={getFilterUrl({category:c})} className={c===category? 'active':''}>{c}</Link>
                                        </li>
                                    ))
                                }
                            </ul>
                        )}
                    </div>
        
        
     

     

     

     

    app.js에 루트 추가해준다. 

                  <Route path="/search/category/:category" component={SearchScreen} exact></Route>
                  <Route path="/search/category/:category/name/:name" component={SearchScreen} exact></Route>
    

     

    productActions에서 다음과 같이 카테고리 항목도 추가해준다. 

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

     

    라우터도 수정해준다. 

    productRouter.get('/' ,expressAsyncHandler(async(req, res)=>{
        const seller = req.query.seller || ''
        const name = req.query.name || ''
        const category = req.query.category || ''
        const sellerFilter = seller? {seller} :{};
        const nameFilter = name? {name:{$regex:name, $options:'i'}} :{};
        const categoryFilter = category? {category} :{};
        const products = await Product.find({...sellerFilter, ...nameFilter, ...categoryFilter}).populate('seller', 'seller.name seller.logo');
        res.send(products);
    }));

     

     

     

    검색을 하고나서 카테고리들어가면 카테고리 안의 name일치하는 것 찾아준다.

     

    이제 app.js에 카테고리 사이드바를 넣자. 

     

     

      const [sidebarIsOpen, setSidebarIsOpen] = useState(false)
    
    
      return (
        <BrowserRouter >
        <div className="grid-container">
              <header className="row">
                <div>
                  <button type="button" className="open-sidebar" onClick={()=>setSidebarIsOpen(true)}>
                    <i className="fa fa-bars"></i>
                  </button>
                  <Link className="brand" to="/">amazona</Link>
                  
                  
                  
                  ...
                  
                  
                  </header>
                <aside className={sidebarIsOpen? 'open': ''}>
                      <ul className="categories">
                        <li>
                          <strong>Categories</strong>
                          <button type="button" className="close-sidebar" onClick={()=>setSidebarIsOpen(false)}>
                              <i className="fa fa-close"></i>
                          </button>
                      </li>
                    </ul>
              </aside>
              <main>

    스타일링하자.

    /* Aside */
    aside {
      position: fixed;
      width: 30rem;
      height: 100%;
      background-color: #efefef;
      z-index: 1000 ;
      transform: translate(-30rem);
      transition: all 0.5s;
    
    }
    aside.open{
      transform: translate(0) ;
    }
    button.open-sidebar{
      font-size: 3rem;
      padding:0.2rem 0.5rem;
      margin:0 0.5rem;
      background:none;
      color: #ffffff;
      cursor:pointer
    }
    button.open-sidebar:hover{
      border-color: #ffffff;
    }
    aside ul{
      padding:0;
      list-style: none;
    }
    aside li{
      display: flex;
      justify-content: space-between;
      padding:1rem;
    }
    button.close-sidebar{
      padding:0.3rem 0.8rem;
    }
    
    
    
    
    
    /* Image */
    

     

     

     

    이제 카테고리가 보이도록하자. 

    searchScreen의 내용을 가지고 온다. 

      const [sidebarIsOpen, setSidebarIsOpen] = useState(false)
    
    
    
      const productCategoryList = useSelector(state => state.productCategoryList)
      const {loading:loadingCategories, error:errorCategories, categories} = productCategoryList
    
      useEffect(() => {
        dispatch(listProductCategories())
      }, [dispatch])
    
    
    
    
    
    ...
              <aside className={sidebarIsOpen? 'open': ''}>
                    <ul className="categories">
                      <li>
                        <strong>Categories</strong>
                        <button type="button" className="close-sidebar" onClick={()=>setSidebarIsOpen(false)}>
                            <i className="fa fa-close"></i>
                          </button>
                      </li>
                      {loadingCategories? <LoadingBox></LoadingBox> :
                        errorCategories? <MessageBox variant='danger'>{errorCategories}</MessageBox> :
                        (
                            <ul>
                                {categories.map(c=>
                                    (
                                        <li key = {c}>
                                            <Link to ={`/search/category/${c}`} onClick={()=>setSidebarIsOpen(false)} >{c}</Link>
                                        </li>
                                    ))
                                }
                            </ul>
                        )}
                    </ul>
              </aside>

     

     

     

     

    클릭하면 잘 이동한다. 

     

    이제 상품 정렬순서기능과 골라내기 기능을 만들어보자. (sort&filter)

     

    searchscreen에 템플릿을 잡아준다. 

    <div className="col-1">
                        <div>
                            <h3>Department</h3>
                            {loadingCategories? <LoadingBox></LoadingBox> :
                            errorCategories? <MessageBox variant='danger'>{errorCategories}</MessageBox> :
                            (
                                <ul>
                                    {categories.map(c=>
                                        (
                                            <li key = {c}>
                                                <Link to ={getFilterUrl({category:c})} className={c===category? 'active':''}>{c}</Link>
                                            </li>
                                        ))
                                    }
                                </ul>
                            )}
                        </div>
                        <div>
                            <h3>Price</h3>
                            <ul>
                                {prices.map(p=>(
                                    <li key={p.name}>
                                        <Link className={`${p.min}-${p.max}`=== `${min}-${max}`? 'active':''} to={getFilterUrl({min:p.min, max:p.max})} >{p.name}</Link>
                                    </li>
                                ))}
                            </ul>
                        </div>
                    </div>

     

    frontend>utils.js를 만들어준다. 

     

    export const prices = [{
        name:'Any',
        min:0,
        max:0
    },{
        name:`$1 to $10`,
        min:1,
        max:10
    },{
        name:`$10 to $100`,
        min:10,
        max:100
    },{
        name:`$100 to $1000`,
        min:100,
        max:1000
    }
    ]
    
    export const ratings = [
        {
            name:'4starts & up',
            rating:4
        },
        {
            name:'3starts & up',
            rating:3
        },
        {
            name:'2starts & up',
            rating:2
        },
        {
            name:'1starts & up',
            rating:1
        },
    ]

     

    다시 search screen와서 작성해준다. 

    import { prices } from '../utils';
    
        const {name = 'all', category='all', min=0, max=0} = useParams();
        
        useEffect(() => {
            dispatch(listProducts({name:name!=='all'? name: '', category:category !=='all'? category:'', min, max}))
        }, [category, dispatch,name,min, max])
    
    	const getFilterUrl = (filter) =>{
            const filterCategory = filter.category ||category;
            const filterName = filter.name ||name;
            const filterMin = filter.min? filter.min :filter.min===0? 0:min;
            const filterMax = filter.max? filter.max :filter.max===0? 0:max;
            return `/search/category/${filterCategory}/name/${filterName}/min/${filterMin}/max/${filterMax}`
        }
    
    
    
                        <div>
                            <h3>Price</h3>
                            <ul>
                                {prices.map(p=>(
                                    <li key={p.name}>
                                        <Link className={`${p.min}-${p.max}`=== `${min}-${max}`? 'active':''} to={getFilterUrl({min:p.min, max:p.max})} >{p.name}</Link>
                                    </li>
                                ))}
                            </ul>
                        </div>

    app.js에 루트 추가

                  <Route path="/search/category/:category/name/:name/min/:min/max/:max" component={SearchScreen} exact></Route>
    

     

     

     

    이제 actions을 업데이트 해준다. 

    (필터 추가)

    여기서 max를 0으로 해놓는 이유는 라우터에서 0과 비교해놓을 것이기 때문이다. 

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

     

    라우터도 수정하자 .

    productRouter.get('/' ,expressAsyncHandler(async(req, res)=>{
        const seller = req.query.seller || ''
        const name = req.query.name || ''
        const category = req.query.category || ''
        const min = req.query.min && Number(req.query.min) !== 0? Number(req.query.min):0
        const max = req.query.max && Number(req.query.max) !== 0? Number(req.query.max):0
    
        const sellerFilter = seller? {seller} :{};
        const nameFilter = name? {name:{$regex:name, $options:'i'}} :{};
        const categoryFilter = category? {category} :{};
        const priceFilter = min && max ? {price: {$gte:min, $lte:max}}:{};
        const products = await Product.find({...sellerFilter, ...nameFilter, ...categoryFilter, ...priceFilter}).populate('seller', 'seller.name seller.logo');
        res.send(products);
    }));
    

     

     

    index.css

    /* Search */
    .search button{
      border-radius:0  0.5rem 0.5rem 0;
      border-right: none;
      margin-right: 0.5rem;
    }
    .search input{ 
      border-radius: 0.5rem 0 0 0.5rem;
      border-right: none;
      margin-left: 0.5rem;
    }
    .active{
      font-weight:bold;
    }

     

     

    잘 찾아진다. 

     

    검색조건 중복적용도 잘 된다. 

     

    이제 Rating으로 필터도 추가해보자. 

    searchscreen의 템플릿

                        <div>
                            <h3>Price</h3>
                            <ul>
                                {prices.map(p=>(
                                    <li key={p.name}>
                                        <Link className={`${p.min}-${p.max}`=== `${min}-${max}`? 'active':''} to={getFilterUrl({min:p.min, max:p.max})} >{p.name}</Link>
                                    </li>
                                ))}
                            </ul>
                        </div>
                        <div>
                            <h3>Avg. Customer Reviews</h3>
                            <ul>
                                {ratings.map(r=>(
                                    <li key={r.name}>
                                        <Link className={`${r.rating}`===`${rating}` 'active':''} to={getFilterUrl({rating:r.rating})} ><Rating></Rating></Link>
                                    </li>
                                ))}
                            </ul>
                        </div>
                    </div>

    여기서 Rating은 component인데 여기서 조금 수정해보자. 

    import React from 'react'
    
    function Rating(props) {
        const {rating, numReviews,caption} =props;
        return (
            <div className="rating">
            <span><i className={ rating>=1? "fa fa-star": rating>=0.5?"fa fa-star-half-o":"fa fa-star-o" }></i></span>
            <span><i className={ rating>=2? "fa fa-star": rating>=1.5?"fa fa-star-half-o":"fa fa-star-o" }></i></span>
            <span><i className={ rating>=3? "fa fa-star": rating>=2.5?"fa fa-star-half-o":"fa fa-star-o" }></i></span>
            <span><i className={ rating>=4? "fa fa-star": rating>=3.5?"fa fa-star-half-o":"fa fa-star-o" }></i></span>
            <span><i className={ rating>=5? "fa fa-star": rating>=4.5?"fa fa-star-half-o":"fa fa-star-o" }></i></span>
            
            <span>{numReviews + ' reviews'}</span>
            {caption? <span>{caption}</span> : <span>{numReviews + 'reviews'}</span>}
        </div> 
        ) 
    }
    
    export default Rating
    

    searchScreen.js

    	const {name = 'all', category='all', min=0, max=0, rating=0} = useParams();
      
        useEffect(() => {
            dispatch(listProducts({name:name!=='all'? name: '', category:category !=='all'? category:'', min, max,rating}))
        }, [category, dispatch,name,min, max,rating])
    
    
        const getFilterUrl = (filter) =>{
            const filterCategory = filter.category ||category;
            const filterName = filter.name ||name;
            const filterMin = filter.min? filter.min :filter.min===0? 0:min;
            const filterMax = filter.max? filter.max :filter.max===0? 0:max;
            const filterRating = filter.rating||rating;
            return `/search/category/${filterCategory}/name/${filterName}/min/${filterMin}/max/${filterMax}/rating/${filterRating}`
        }
        
                            <div>
                            <h3>Avg. Customer Reviews</h3>
                            <ul>
                                {ratings.map(r=>(
                                    <li key={r.name}>
                                        <Link className={`${r.rating}`===`${rating}`? 'active':''} to={getFilterUrl({rating:r.rating})} ><Rating caption={" & up"} rating={r.rating}></Rating></Link>
                                    </li>
                                ))}
                            </ul>
                        </div>

     

    product actions

     

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

     

     

    productRouter

    productRouter.get('/' ,expressAsyncHandler(async(req, res)=>{
        const seller = req.query.seller || ''
        const name = req.query.name || ''
        const category = req.query.category || ''
        const min = req.query.min && Number(req.query.min) !== 0? Number(req.query.min):0
        const max = req.query.max && Number(req.query.max) !== 0? Number(req.query.max):0
        const rating = req.query.rating && Number(req.query.rating) !== 0? Number(req.query.rating):0
    
        const sellerFilter = seller? {seller} :{};
        const nameFilter = name? {name:{$regex:name, $options:'i'}} :{};
        const categoryFilter = category? {category} :{};
        const priceFilter = min && max ? {price: {$gte:min, $lte:max}}:{};
        const ratingFilter = rating ? {rating: {$gte:rating}}:{};
        const products = await Product.find({...sellerFilter, ...nameFilter, ...categoryFilter, ...priceFilter, ...ratingFilter}).populate('seller', 'seller.name seller.logo');
        res.send(products);
    }));

    app.js에 루트 추가(있던거에 rating만 추가)

     

                  <Route path="/search/category/:category/name/:name/min/:min/max/:max/rating/:rating" component={SearchScreen} exact></Route>
    

     

     any카테고리를 넣자. 

    searchScreen.js

     

                        <div>
                            <h3>Department</h3>
                            {loadingCategories? <LoadingBox></LoadingBox> :
                            errorCategories? <MessageBox variant='danger'>{errorCategories}</MessageBox> :
                            (
                                <ul>
                                    <li>
                                        <Link to ={getFilterUrl({category:'all'})} className={'all'==='category'? 'active':''}>Any</Link>
                                    </li>
                                    {categories.map(c=>
                                        (
                                            <li key = {c}>
                                                <Link to ={getFilterUrl({category:c})} className={c===category? 'active':''}>{c}</Link>
                                            </li>
                                        ))
                                    }
                                </ul>
                            )}
                        </div>

     

    이제 sorting을 넣어보자. 

    sorting은 selectbox가 될것이다. 

     

        const {name = 'all', category='all', min=0, max=0, rating=0, order='newest'} = useParams();
       useEffect(() => {
            dispatch(listProducts({name:name!=='all'? name: '', category:category !=='all'? category:'', min, max,rating,order}))
        }, [category, dispatch,name,min, max,rating,order])
        const getFilterUrl = (filter) =>{
            const filterCategory = filter.category ||category;
            const filterName = filter.name ||name;
            const filterMin = filter.min? filter.min :filter.min===0? 0:min;
            const filterMax = filter.max? filter.max :filter.max===0? 0:max;
            const filterRating = filter.rating||rating;
            const sortOrder = filter.order||order;
            return `/search/category/${filterCategory}/name/${filterName}/min/${filterMin}/max/${filterMax}/rating/${filterRating}/order/${sortOrder}`
        }
        
        
        
        
     			<div className="row">
                    {loading? <LoadingBox></LoadingBox> :
                    error? <MessageBox variant='danger'>{error}</MessageBox> :
                    (<div>
                        {products.length} Results
                    </div>
                    )}
                    <div>
                        Sort by {' '}
                        <select value = {order} onChange={e=>props.history.push(getFilterUrl({order:e.target.value}))}>
                            <option value="newest">Newest Arrivals</option>
                            <option value="lowest">Price: Low to High</option>
                            <option value="highest">Price: High to Low</option>
                            <option value="toprated">Avg. Customer Reviews</option>
                        </select>
                    </div>
                </div>

     

    productActions

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

     

     

    productRouter

    productRouter.get('/' ,expressAsyncHandler(async(req, res)=>{
        const seller = req.query.seller || ''
        const name = req.query.name || ''
        const category = req.query.category || ''
        const min = req.query.min && Number(req.query.min) !== 0? Number(req.query.min):0
        const max = req.query.max && Number(req.query.max) !== 0? Number(req.query.max):0
        const rating = req.query.rating && Number(req.query.rating) !== 0? Number(req.query.rating):0
        const order = req.query.order || ''
    
        const sellerFilter = seller? {seller} :{};
        const nameFilter = name? {name:{$regex:name, $options:'i'}} :{};
        const categoryFilter = category? {category} :{};
        const priceFilter = min && max ? {price: {$gte:min, $lte:max}}:{};
        const ratingFilter = rating ? {rating: {$gte:rating}}:{};
        const sortOrder = order==='lowest'? {price:1} : 
                          order==='highest'? {price:-1} :
                          order==='toprated'? {rating:-1} :
                          {_id:-1};
    
        const products = await Product.find({...sellerFilter, ...nameFilter, ...categoryFilter, ...priceFilter, ...ratingFilter}).populate('seller', 'seller.name seller.logo').sort(sortOrder);
        res.send(products);
    }));

    app.js에 원래 있던거에 Order 추가

                  <Route path="/search/category/:category/name/:name/min/:min/max/:max/rating/:rating/order/:order" component={SearchScreen} exact></Route>
    

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.