Skip to content

Commit

Permalink
feat: filter category
Browse files Browse the repository at this point in the history
  • Loading branch information
Ngọ Nhâm committed Nov 8, 2024
1 parent 1ed9056 commit cc491fe
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 59 deletions.
10 changes: 10 additions & 0 deletions src/apis/category.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { pathApi } from 'src/constants/path'
import { Category } from 'src/types/category.type'
import { ResponseApi } from 'src/types/utils.type'
import http from 'src/utils/http'
const categoryAPi = {
getCategories() {
return http.get<ResponseApi<Category[]>>(pathApi.categories)
}
}
export default categoryAPi
3 changes: 2 additions & 1 deletion src/constants/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const pathApi = {
login: 'api/Auth/sign-in',
register: 'api/Auth/sign-up',
logout: 'api/Auth/sign-out',
products: 'api/Product/products'
products: 'api/Product/products',
categories: 'api/Category/categoies'
} as const
export default path
4 changes: 2 additions & 2 deletions src/constants/product.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export const _orderBy = {
export const orderBy = {
createAt: 'createAt',
view: 'view',
sold: 'sold',
price: 'price'
} as const
export const _order = {
export const order = {
asc: 'asc',
desc: 'desc'
} as const
83 changes: 51 additions & 32 deletions src/pages/ProductList/AsideFilter/AsideFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import { Link } from 'react-router-dom'
import { createSearchParams, Link } from 'react-router-dom'
import Button from 'src/components/Button'
import Input from 'src/components/input'
import path from 'src/constants/path'
import path, { pathApi } from 'src/constants/path'
import { QueryConfig } from '../ProductList'
import { Category } from 'src/types/category.type'
import classNames from 'classnames'
import { omit } from 'lodash'

export default function AsideFilter() {
interface Props {
queryConfig: QueryConfig
categories: Category[]
}

export default function AsideFilter({ queryConfig, categories }: Props) {
const { categoryId } = queryConfig
console.log('queryConfig', queryConfig)
return (
<div className='py-4'>
<Link to={path.home} className='flex items-center font-bold'>
<Link
to={path.home}
className={classNames('flex items-center font-bold', {
'text-orange': !categoryId
})}
>
<svg viewBox='0 0 12 10' className='w-3 h-4 mr-3 fill-current'>
<g fillRule='evenodd' stroke='none' strokeWidth={1}>
<g transform='translate(-373 -208)'>
Expand All @@ -24,34 +40,37 @@ export default function AsideFilter() {
</Link>
<div className='bg-gray-300 h-[1px] my-4' />
<ul>
<li className='py-2 pl-2'>
<Link to={path.home} className='relative px-2 text-orange font-semibold'>
<svg viewBox='0 0 4 7' className='fill-orange h-2 w-2 absolute top-1 left-[-10px]'>
<polygon points='4 3.5 0 0 0 7' />
</svg>
Thời trang nam
</Link>
</li>
<li className='py-2 pl-2'>
<Link to={path.home} className='relative px-2'>
Áo Khoác
</Link>
</li>
<li className='py-2 pl-2'>
<Link to={path.home} className='relative px-2'>
Áo Khoác
</Link>
</li>
<li className='py-2 pl-2'>
<Link to={path.home} className='relative px-2'>
Áo Vest và Blazer
</Link>
</li>
<li className='py-2 pl-2'>
<Link to={path.home} className='relative px-2'>
Áo Hoodie, Áo Len & Áo Nỉ
</Link>
</li>
{categories.map((categoryItem) => {
const isActive = categoryItem.id === categoryId
return (
<li className='py-2 pl-2' key={categoryItem.id}>
<Link
to={{
pathname: pathApi.home,
search: createSearchParams(
omit(
{
...queryConfig,
categoryId: categoryItem.id
},
['page']
)
).toString()
}}
className={classNames('relative px-2', {
'text-orange font-semibold': isActive
})}
>
{isActive && (
<svg viewBox='0 0 4 7' className='fill-orange h-2 w-2 absolute top-1 left-[-10px]'>
<polygon points='4 3.5 0 0 0 7' />
</svg>
)}
{categoryItem.name}
</Link>
</li>
)
})}
</ul>
<Link to={path.home} className='flex items-center font-bold mt-4 uppercase'>
<svg
Expand Down
22 changes: 15 additions & 7 deletions src/pages/ProductList/ProductList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import useQueryParams from 'src/hooks/useQueryParams'
import productApi from 'src/apis/product.api'
import Pagination from 'src/components/Pagination'
import { ProductListConfig } from 'src/types/product.type'
import categoryAPi from 'src/apis/category.api'

export type QueryConfig = {
[key in keyof ProductListConfig]: string
Expand All @@ -17,42 +18,49 @@ export default function ProductList() {
const queryConfig: QueryConfig = omitBy(
{
page: queryParams.page || '1',
pageSize: queryParams.pageSize || 1,
pageSize: queryParams.pageSize || 15,
orderBy: queryParams.orderBy,
exclude: queryParams.exclude,
order: queryParams.order,
categoryId: queryParams.categoryId,
name: queryParams.name,
ascending: queryParams.ascending,
price_max: queryParams.price_max,
price_min: queryParams.price_min
},
isUndefined
)
const { data } = useQuery({
const { data: productsData } = useQuery({
queryKey: ['products', queryParams],
queryFn: () => {
return productApi.getProducts(queryConfig as ProductListConfig)
},
placeholderData: keepPreviousData
})
const { data: categoriesData } = useQuery({
queryKey: ['categoies'],
queryFn: () => {
return categoryAPi.getCategories()
}
})
return (
<div className='bg-gray-200 py-6'>
<div className='container'>
{data && (
{productsData && (
<div className='grid grid-cols-12 gap-6'>
<div className='col-span-3'>
<AsideFilter />
<AsideFilter queryConfig={queryConfig} categories={categoriesData?.data.response || []} />
</div>
<div className='col-span-9'>
<SortProductList queryConfig={queryConfig} pageSize={data.data.response?.totalPages || 1} />
<SortProductList queryConfig={queryConfig} pageSize={productsData.data.response?.totalPages || 1} />
<div className='mt-6 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3'>
{data?.data.response?.items.map((product) => (
{productsData?.data.response?.items.map((product) => (
<div className='col-span-1' key={product.id}>
<Product product={product} />
</div>
))}
</div>
<Pagination queryConfig={queryConfig} pageSize={data.data.response?.totalPages || 1} />
<Pagination queryConfig={queryConfig} pageSize={productsData.data.response?.totalPages || 1} />
</div>
</div>
)}
Expand Down
25 changes: 13 additions & 12 deletions src/pages/ProductList/SortProductList/SortProductList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ProductListConfig } from 'src/types/product.type'
import { QueryConfig } from '../ProductList'
import classNames from 'classnames'
import { _orderBy } from 'src/constants/product'
import { orderBy as orderByConstant } from 'src/constants/product'
import { order as orderConstant } from 'src/constants/product'
import { createSearchParams, Link, useNavigate } from 'react-router-dom'
import path from 'src/constants/path'
import { omit } from 'lodash'
Expand All @@ -11,7 +12,7 @@ interface Props {
pageSize: number
}
export default function SortProductList({ queryConfig, pageSize }: Props) {
const { orderBy = _orderBy.createAt, page } = queryConfig
const { orderBy = orderByConstant.createAt, page } = queryConfig
const { order } = queryConfig
const pageNumber = Number(page)
const navigate = useNavigate()
Expand Down Expand Up @@ -41,7 +42,7 @@ export default function SortProductList({ queryConfig, pageSize }: Props) {
{
...queryConfig,
order: String(orderValue),
orderBy: _orderBy.price
orderBy: orderByConstant.price
},
['page']
)
Expand All @@ -56,47 +57,47 @@ export default function SortProductList({ queryConfig, pageSize }: Props) {
<button
className={classNames(
'h-8 px-4 capitalize text-sm rounded-sm',
isActiveOrderBy(_orderBy.view)
isActiveOrderBy(orderByConstant.createAt)
? 'bg-orange text-white hover:bg-orange/80'
: 'bg-white text-black hover:bg-slate-100'
)}
onClick={() => handleSort(_orderBy.view)}
onClick={() => handleSort(orderByConstant.createAt)}
>
Phổ biến
</button>
<button
className={classNames(
'h-8 px-4 capitalize text-sm rounded-sm',
isActiveOrderBy(_orderBy.createAt)
isActiveOrderBy(orderByConstant.view)
? 'bg-orange text-white hover:bg-orange/80'
: 'bg-white text-black hover:bg-slate-100'
)}
onClick={() => handleSort(_orderBy.createAt)}
onClick={() => handleSort(orderByConstant.view)}
>
Mới nhất
</button>
<button
className={classNames(
'h-8 px-4 capitalize text-sm rounded-sm',
isActiveOrderBy(_orderBy.sold)
isActiveOrderBy(orderByConstant.sold)
? 'bg-orange text-white hover:bg-orange/80'
: 'bg-white text-black hover:bg-slate-100'
)}
onClick={() => handleSort(_orderBy.sold)}
onClick={() => handleSort(orderByConstant.sold)}
>
Bán chạy
</button>
<select
className='h-8 px-4 bg-white text-left text-sm outline-none capitalize border border-gray-300 rounded-sm shadow-sm focus:border-gray-400'
title='Giá'
defaultValue={order}
onChange={(event) => handlePriceOrder(event.target.value === 'true')}
onChange={(event) => handlePriceOrder(event.target.value as Exclude<ProductListConfig['order'], undefined>)}
>
<option value='' disabled hidden>
Giá
</option>
<option value={'true'}>Giá: Thấp đến cao</option>
<option value={'false'}>Giá: Cao đến Thấp</option>
<option value={orderConstant.desc}>Giá: Thấp đến cao</option>
<option value={orderConstant.asc}>Giá: Cao đến Thấp</option>
</select>
</div>
<div className='flex items-center'>
Expand Down
4 changes: 4 additions & 0 deletions src/types/category.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Category {
id: string
name: string
}
8 changes: 3 additions & 5 deletions src/types/product.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ export interface Product {
name: string
image: string
images: string[]
cateogry: {
name: string
id: string
}
cateogryId?: string
}
export interface ProductList {
items: Product[]
Expand All @@ -26,11 +23,12 @@ export interface ProductListConfig {
page?: number | string
pageSize?: number
orderBy?: 'createAt' | 'view' | 'sold' | 'price'
order?: boolean
order?: 'asc' | 'desc'
ascending?: boolean
exclude?: string
rating_filter?: number | string
price_max?: number | string
price_min?: number | string
name?: string
categoryId?: string
}

0 comments on commit cc491fe

Please sign in to comment.